Merge remote-tracking branch 'origin/blender-v2.93-release'
[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_array.h"
25 #include "util/util_half.h"
26 #include "util/util_string.h"
27 #include "util/util_texture.h"
28 #include "util/util_types.h"
29 #include "util/util_vector.h"
30
31 CCL_NAMESPACE_BEGIN
32
33 class Device;
34
35 enum MemoryType {
36   MEM_READ_ONLY,
37   MEM_READ_WRITE,
38   MEM_DEVICE_ONLY,
39   MEM_GLOBAL,
40   MEM_TEXTURE,
41   MEM_PIXELS
42 };
43
44 /* Supported Data Types */
45
46 enum DataType {
47   TYPE_UNKNOWN,
48   TYPE_UCHAR,
49   TYPE_UINT16,
50   TYPE_UINT,
51   TYPE_INT,
52   TYPE_FLOAT,
53   TYPE_HALF,
54   TYPE_UINT64,
55 };
56
57 static inline size_t datatype_size(DataType datatype)
58 {
59   switch (datatype) {
60     case TYPE_UNKNOWN:
61       return 1;
62     case TYPE_UCHAR:
63       return sizeof(uchar);
64     case TYPE_FLOAT:
65       return sizeof(float);
66     case TYPE_UINT:
67       return sizeof(uint);
68     case TYPE_UINT16:
69       return sizeof(uint16_t);
70     case TYPE_INT:
71       return sizeof(int);
72     case TYPE_HALF:
73       return sizeof(half);
74     case TYPE_UINT64:
75       return sizeof(uint64_t);
76     default:
77       return 0;
78   }
79 }
80
81 /* Traits for data types */
82
83 template<typename T> struct device_type_traits {
84   static const DataType data_type = TYPE_UNKNOWN;
85   static const int num_elements = sizeof(T);
86 };
87
88 template<> struct device_type_traits<uchar> {
89   static const DataType data_type = TYPE_UCHAR;
90   static const int num_elements = 1;
91 };
92
93 template<> struct device_type_traits<uchar2> {
94   static const DataType data_type = TYPE_UCHAR;
95   static const int num_elements = 2;
96 };
97
98 template<> struct device_type_traits<uchar3> {
99   static const DataType data_type = TYPE_UCHAR;
100   static const int num_elements = 3;
101 };
102
103 template<> struct device_type_traits<uchar4> {
104   static const DataType data_type = TYPE_UCHAR;
105   static const int num_elements = 4;
106 };
107
108 template<> struct device_type_traits<uint> {
109   static const DataType data_type = TYPE_UINT;
110   static const int num_elements = 1;
111 };
112
113 template<> struct device_type_traits<uint2> {
114   static const DataType data_type = TYPE_UINT;
115   static const int num_elements = 2;
116 };
117
118 template<> struct device_type_traits<uint3> {
119   static const DataType data_type = TYPE_UINT;
120   static const int num_elements = 3;
121 };
122
123 template<> struct device_type_traits<uint4> {
124   static const DataType data_type = TYPE_UINT;
125   static const int num_elements = 4;
126 };
127
128 template<> struct device_type_traits<int> {
129   static const DataType data_type = TYPE_INT;
130   static const int num_elements = 1;
131 };
132
133 template<> struct device_type_traits<int2> {
134   static const DataType data_type = TYPE_INT;
135   static const int num_elements = 2;
136 };
137
138 template<> struct device_type_traits<int3> {
139   static const DataType data_type = TYPE_INT;
140   static const int num_elements = 3;
141 };
142
143 template<> struct device_type_traits<int4> {
144   static const DataType data_type = TYPE_INT;
145   static const int num_elements = 4;
146 };
147
148 template<> struct device_type_traits<float> {
149   static const DataType data_type = TYPE_FLOAT;
150   static const int num_elements = 1;
151 };
152
153 template<> struct device_type_traits<float2> {
154   static const DataType data_type = TYPE_FLOAT;
155   static const int num_elements = 2;
156 };
157
158 template<> struct device_type_traits<float3> {
159   static const DataType data_type = TYPE_FLOAT;
160   static const int num_elements = 4;
161 };
162
163 template<> struct device_type_traits<float4> {
164   static const DataType data_type = TYPE_FLOAT;
165   static const int num_elements = 4;
166 };
167
168 template<> struct device_type_traits<half> {
169   static const DataType data_type = TYPE_HALF;
170   static const int num_elements = 1;
171 };
172
173 template<> struct device_type_traits<ushort4> {
174   static const DataType data_type = TYPE_UINT16;
175   static const int num_elements = 4;
176 };
177
178 template<> struct device_type_traits<uint16_t> {
179   static const DataType data_type = TYPE_UINT16;
180   static const int num_elements = 1;
181 };
182
183 template<> struct device_type_traits<half4> {
184   static const DataType data_type = TYPE_HALF;
185   static const int num_elements = 4;
186 };
187
188 template<> struct device_type_traits<uint64_t> {
189   static const DataType data_type = TYPE_UINT64;
190   static const int num_elements = 1;
191 };
192
193 /* Device Memory
194  *
195  * Base class for all device memory. This should not be allocated directly,
196  * instead the appropriate subclass can be used. */
197
198 class device_memory {
199  public:
200   size_t memory_size()
201   {
202     return data_size * data_elements * datatype_size(data_type);
203   }
204   size_t memory_elements_size(int elements)
205   {
206     return elements * data_elements * datatype_size(data_type);
207   }
208
209   /* Data information. */
210   DataType data_type;
211   int data_elements;
212   size_t data_size;
213   size_t device_size;
214   size_t data_width;
215   size_t data_height;
216   size_t data_depth;
217   MemoryType type;
218   const char *name;
219
220   /* Pointers. */
221   Device *device;
222   device_ptr device_pointer;
223   void *host_pointer;
224   void *shared_pointer;
225   /* reference counter for shared_pointer */
226   int shared_counter;
227
228   virtual ~device_memory();
229
230   void swap_device(Device *new_device, size_t new_device_size, device_ptr new_device_ptr);
231   void restore_device();
232
233   bool is_resident(Device *sub_device) const;
234
235  protected:
236   friend class CUDADevice;
237   friend class OptiXDevice;
238
239   /* Only create through subclasses. */
240   device_memory(Device *device, const char *name, MemoryType type);
241   device_memory(device_memory &&other) noexcept;
242
243   /* No copying allowed. */
244   device_memory(const device_memory &) = delete;
245   device_memory &operator=(const device_memory &) = delete;
246
247   /* Host allocation on the device. All host_pointer memory should be
248    * allocated with these functions, for devices that support using
249    * the same pointer for host and device. */
250   void *host_alloc(size_t size);
251   void host_free();
252
253   /* Device memory allocation and copying. */
254   void device_alloc();
255   void device_free();
256   void device_copy_to();
257   void device_copy_from(int y, int w, int h, int elem);
258   void device_zero();
259
260   device_ptr original_device_ptr;
261   size_t original_device_size;
262   Device *original_device;
263   bool need_realloc_;
264   bool modified;
265 };
266
267 /* Device Only Memory
268  *
269  * Working memory only needed by the device, with no corresponding allocation
270  * on the host. Only used internally in the device implementations. */
271
272 template<typename T> class device_only_memory : public device_memory {
273  public:
274   device_only_memory(Device *device, const char *name, bool allow_host_memory_fallback = false)
275       : device_memory(device, name, allow_host_memory_fallback ? MEM_READ_WRITE : MEM_DEVICE_ONLY)
276   {
277     data_type = device_type_traits<T>::data_type;
278     data_elements = max(device_type_traits<T>::num_elements, 1);
279   }
280
281   device_only_memory(device_only_memory &&other) noexcept : device_memory(std::move(other))
282   {
283   }
284
285   virtual ~device_only_memory()
286   {
287     free();
288   }
289
290   void alloc_to_device(size_t num, bool shrink_to_fit = true)
291   {
292     size_t new_size = num;
293     bool reallocate;
294
295     if (shrink_to_fit) {
296       reallocate = (data_size != new_size);
297     }
298     else {
299       reallocate = (data_size < new_size);
300     }
301
302     if (reallocate) {
303       device_free();
304       data_size = new_size;
305       device_alloc();
306     }
307   }
308
309   void free()
310   {
311     device_free();
312     data_size = 0;
313   }
314
315   void zero_to_device()
316   {
317     device_zero();
318   }
319 };
320
321 /* Device Vector
322  *
323  * Data vector to exchange data between host and device. Memory will be
324  * allocated on the host first with alloc() and resize, and then filled
325  * in and copied to the device with copy_to_device(). Or alternatively
326  * allocated and set to zero on the device with zero_to_device().
327  *
328  * When using memory type MEM_GLOBAL, a pointer to this memory will be
329  * automatically attached to kernel globals, using the provided name
330  * matching an entry in kernel_textures.h. */
331
332 template<typename T> class device_vector : public device_memory {
333  public:
334   device_vector(Device *device, const char *name, MemoryType type)
335       : device_memory(device, name, type)
336   {
337     data_type = device_type_traits<T>::data_type;
338     data_elements = device_type_traits<T>::num_elements;
339     modified = true;
340     need_realloc_ = true;
341
342     assert(data_elements > 0);
343   }
344
345   virtual ~device_vector()
346   {
347     free();
348   }
349
350   /* Host memory allocation. */
351   T *alloc(size_t width, size_t height = 0, size_t depth = 0)
352   {
353     size_t new_size = size(width, height, depth);
354
355     if (new_size != data_size) {
356       device_free();
357       host_free();
358       host_pointer = host_alloc(sizeof(T) * new_size);
359       modified = true;
360       assert(device_pointer == 0);
361     }
362
363     data_size = new_size;
364     data_width = width;
365     data_height = height;
366     data_depth = depth;
367
368     return data();
369   }
370
371   /* Host memory resize. Only use this if the original data needs to be
372    * preserved, it is faster to call alloc() if it can be discarded. */
373   T *resize(size_t width, size_t height = 0, size_t depth = 0)
374   {
375     size_t new_size = size(width, height, depth);
376
377     if (new_size != data_size) {
378       void *new_ptr = host_alloc(sizeof(T) * new_size);
379
380       if (new_size && data_size) {
381         size_t min_size = ((new_size < data_size) ? new_size : data_size);
382         memcpy((T *)new_ptr, (T *)host_pointer, sizeof(T) * min_size);
383       }
384
385       device_free();
386       host_free();
387       host_pointer = new_ptr;
388       assert(device_pointer == 0);
389     }
390
391     data_size = new_size;
392     data_width = width;
393     data_height = height;
394     data_depth = depth;
395
396     return data();
397   }
398
399   /* Take over data from an existing array. */
400   void steal_data(array<T> &from)
401   {
402     device_free();
403     host_free();
404
405     data_size = from.size();
406     data_width = 0;
407     data_height = 0;
408     data_depth = 0;
409     host_pointer = from.steal_pointer();
410     assert(device_pointer == 0);
411   }
412
413   void give_data(array<T> &to)
414   {
415     device_free();
416
417     to.set_data((T *)host_pointer, data_size);
418     data_size = 0;
419     data_width = 0;
420     data_height = 0;
421     data_depth = 0;
422     host_pointer = 0;
423     assert(device_pointer == 0);
424   }
425
426   /* Free device and host memory. */
427   void free()
428   {
429     device_free();
430     host_free();
431
432     data_size = 0;
433     data_width = 0;
434     data_height = 0;
435     data_depth = 0;
436     host_pointer = 0;
437     modified = true;
438     need_realloc_ = true;
439     assert(device_pointer == 0);
440   }
441
442   void free_if_need_realloc(bool force_free)
443   {
444     if (need_realloc_ || force_free) {
445       free();
446     }
447   }
448
449   bool is_modified() const
450   {
451     return modified;
452   }
453
454   bool need_realloc()
455   {
456     return need_realloc_;
457   }
458
459   void tag_modified()
460   {
461     modified = true;
462   }
463
464   void tag_realloc()
465   {
466     need_realloc_ = true;
467     tag_modified();
468   }
469
470   size_t size() const
471   {
472     return data_size;
473   }
474
475   T *data()
476   {
477     return (T *)host_pointer;
478   }
479
480   T &operator[](size_t i)
481   {
482     assert(i < data_size);
483     return data()[i];
484   }
485
486   void copy_to_device()
487   {
488     if (data_size != 0) {
489       device_copy_to();
490     }
491   }
492
493   void copy_to_device_if_modified()
494   {
495     if (!modified) {
496       return;
497     }
498
499     copy_to_device();
500   }
501
502   void clear_modified()
503   {
504     modified = false;
505     need_realloc_ = false;
506   }
507
508   void copy_from_device()
509   {
510     device_copy_from(0, data_width, data_height, sizeof(T));
511   }
512
513   void copy_from_device(int y, int w, int h)
514   {
515     device_copy_from(y, w, h, sizeof(T));
516   }
517
518   void zero_to_device()
519   {
520     device_zero();
521   }
522
523   void move_device(Device *new_device)
524   {
525     copy_from_device();
526     device_free();
527     device = new_device;
528     copy_to_device();
529   }
530
531  protected:
532   size_t size(size_t width, size_t height, size_t depth)
533   {
534     return width * ((height == 0) ? 1 : height) * ((depth == 0) ? 1 : depth);
535   }
536 };
537
538 /* Pixel Memory
539  *
540  * Device memory to efficiently draw as pixels to the screen in interactive
541  * rendering. Only copying pixels from the device is supported, not copying to. */
542
543 template<typename T> class device_pixels : public device_vector<T> {
544  public:
545   device_pixels(Device *device, const char *name) : device_vector<T>(device, name, MEM_PIXELS)
546   {
547   }
548
549   void alloc_to_device(size_t width, size_t height, size_t depth = 0)
550   {
551     device_vector<T>::alloc(width, height, depth);
552
553     if (!device_memory::device_pointer) {
554       device_memory::device_alloc();
555     }
556   }
557
558   T *copy_from_device(int y, int w, int h)
559   {
560     device_memory::device_copy_from(y, w, h, sizeof(T));
561     return device_vector<T>::data();
562   }
563 };
564
565 /* Device Sub Memory
566  *
567  * Pointer into existing memory. It is not allocated separately, but created
568  * from an already allocated base memory. It is freed automatically when it
569  * goes out of scope, which should happen before base memory is freed.
570  *
571  * Note: some devices require offset and size of the sub_ptr to be properly
572  * aligned to device->mem_address_alingment(). */
573
574 class device_sub_ptr {
575  public:
576   device_sub_ptr(device_memory &mem, int offset, int size);
577   ~device_sub_ptr();
578
579   device_ptr operator*() const
580   {
581     return ptr;
582   }
583
584  protected:
585   /* No copying. */
586   device_sub_ptr &operator=(const device_sub_ptr &);
587
588   Device *device;
589   device_ptr ptr;
590 };
591
592 /* Device Texture
593  *
594  * 2D or 3D image texture memory. */
595
596 class device_texture : public device_memory {
597  public:
598   device_texture(Device *device,
599                  const char *name,
600                  const uint slot,
601                  ImageDataType image_data_type,
602                  InterpolationType interpolation,
603                  ExtensionType extension);
604   ~device_texture();
605
606   void *alloc(const size_t width, const size_t height, const size_t depth = 0);
607   void copy_to_device();
608
609   uint slot;
610   TextureInfo info;
611
612  protected:
613   size_t size(const size_t width, const size_t height, const size_t depth)
614   {
615     return width * ((height == 0) ? 1 : height) * ((depth == 0) ? 1 : depth);
616   }
617 };
618
619 CCL_NAMESPACE_END
620
621 #endif /* __DEVICE_MEMORY_H__ */