xref: /third_party/mesa3d/src/vulkan/util/vk_alloc.h (revision bf215546)
1/*
2 * Copyright © 2015 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23#ifndef VK_ALLOC_H
24#define VK_ALLOC_H
25
26/* common allocation inlines for vulkan drivers */
27
28#include <stdio.h>
29#include <string.h>
30#include <vulkan/vulkan.h>
31
32#include "util/u_math.h"
33#include "util/macros.h"
34#include "util/u_printf.h"
35
36#ifdef __cplusplus
37extern "C" {
38#endif
39
40const VkAllocationCallbacks *
41vk_default_allocator(void);
42
43static inline void *
44vk_alloc(const VkAllocationCallbacks *alloc,
45         size_t size, size_t align,
46         VkSystemAllocationScope scope)
47{
48   return alloc->pfnAllocation(alloc->pUserData, size, align, scope);
49}
50
51static inline void *
52vk_zalloc(const VkAllocationCallbacks *alloc,
53          size_t size, size_t align,
54          VkSystemAllocationScope scope)
55{
56   void *mem = vk_alloc(alloc, size, align, scope);
57   if (mem == NULL)
58      return NULL;
59
60   memset(mem, 0, size);
61
62   return mem;
63}
64
65static inline void *
66vk_realloc(const VkAllocationCallbacks *alloc,
67           void *ptr, size_t size, size_t align,
68           VkSystemAllocationScope scope)
69{
70   return alloc->pfnReallocation(alloc->pUserData, ptr, size, align, scope);
71}
72
73static inline void
74vk_free(const VkAllocationCallbacks *alloc, void *data)
75{
76   if (data == NULL)
77      return;
78
79   alloc->pfnFree(alloc->pUserData, data);
80}
81
82static inline char *
83vk_strdup(const VkAllocationCallbacks *alloc, const char *s,
84          VkSystemAllocationScope scope)
85{
86   if (s == NULL)
87      return NULL;
88
89   size_t size = strlen(s) + 1;
90   char *copy = (char *)vk_alloc(alloc, size, 1, scope);
91   if (copy == NULL)
92      return NULL;
93
94   memcpy(copy, s, size);
95
96   return copy;
97}
98
99static inline char *
100vk_vasprintf(const VkAllocationCallbacks *alloc,
101             VkSystemAllocationScope scope,
102             const char *fmt, va_list args)
103{
104   size_t size = u_printf_length(fmt, args) + 1;
105   char *ptr = (char *)vk_alloc(alloc, size, 1, scope);
106   if (ptr != NULL)
107      vsnprintf(ptr, size, fmt, args);
108
109   return ptr;
110}
111
112PRINTFLIKE(3, 4) static inline char *
113vk_asprintf(const VkAllocationCallbacks *alloc,
114            VkSystemAllocationScope scope,
115            const char *fmt, ...)
116{
117   va_list args;
118   va_start(args, fmt);
119   char *ptr = vk_vasprintf(alloc, scope, fmt, args);
120   va_end(args);
121
122   return ptr;
123}
124
125static inline void *
126vk_alloc2(const VkAllocationCallbacks *parent_alloc,
127          const VkAllocationCallbacks *alloc,
128          size_t size, size_t align,
129          VkSystemAllocationScope scope)
130{
131   if (alloc)
132      return vk_alloc(alloc, size, align, scope);
133   else
134      return vk_alloc(parent_alloc, size, align, scope);
135}
136
137static inline void *
138vk_zalloc2(const VkAllocationCallbacks *parent_alloc,
139           const VkAllocationCallbacks *alloc,
140           size_t size, size_t align,
141           VkSystemAllocationScope scope)
142{
143   void *mem = vk_alloc2(parent_alloc, alloc, size, align, scope);
144   if (mem == NULL)
145      return NULL;
146
147   memset(mem, 0, size);
148
149   return mem;
150}
151
152static inline void
153vk_free2(const VkAllocationCallbacks *parent_alloc,
154         const VkAllocationCallbacks *alloc,
155         void *data)
156{
157   if (alloc)
158      vk_free(alloc, data);
159   else
160      vk_free(parent_alloc, data);
161}
162
163/* A multi-pointer allocator
164 *
165 * When copying data structures from the user (such as a render pass), it's
166 * common to need to allocate data for a bunch of different things.  Instead
167 * of doing several allocations and having to handle all of the error checking
168 * that entails, it can be easier to do a single allocation.  This struct
169 * helps facilitate that.  The intended usage looks like this:
170 *
171 *    VK_MULTIALLOC(ma)
172 *    vk_multialloc_add(&ma, &main_ptr, 1);
173 *    vk_multialloc_add(&ma, &substruct1, substruct1Count);
174 *    vk_multialloc_add(&ma, &substruct2, substruct2Count);
175 *
176 *    if (!vk_multialloc_alloc(&ma, pAllocator, VK_ALLOCATION_SCOPE_FOO))
177 *       return vk_error(VK_ERROR_OUT_OF_HOST_MEORY);
178 */
179struct vk_multialloc {
180    size_t size;
181    size_t align;
182
183    uint32_t ptr_count;
184    void **ptrs[12];
185};
186
187#define VK_MULTIALLOC(_name) \
188   struct vk_multialloc _name = { 0, }
189
190static ALWAYS_INLINE void
191vk_multialloc_add_size_align(struct vk_multialloc *ma,
192                             void **ptr, size_t size, size_t align)
193{
194   assert(util_is_power_of_two_nonzero(align));
195   if (size == 0) {
196      *ptr = NULL;
197      return;
198   }
199
200   size_t offset = ALIGN_POT(ma->size, align);
201   ma->size = offset + size;
202   ma->align = MAX2(ma->align, align);
203
204   /* Store the offset in the pointer. */
205   *ptr = (void *)(uintptr_t)offset;
206
207   assert(ma->ptr_count < ARRAY_SIZE(ma->ptrs));
208   ma->ptrs[ma->ptr_count++] = ptr;
209}
210
211#define vk_multialloc_add_size(_ma, _ptr, _type, _size) \
212   do { \
213      _type **_tmp = (_ptr); \
214      (void)_tmp; \
215      vk_multialloc_add_size_align((_ma), (void **)(_ptr), \
216                                   (_size), alignof(_type)); \
217   } while(0)
218
219#define vk_multialloc_add(_ma, _ptr, _type, _count) \
220   vk_multialloc_add_size(_ma, _ptr, _type, (_count) * sizeof(**(_ptr)));
221
222#define VK_MULTIALLOC_DECL_SIZE(_ma, _type, _name, _size) \
223   _type *_name; \
224   vk_multialloc_add_size(_ma, &_name, _type, _size);
225
226#define VK_MULTIALLOC_DECL(_ma, _type, _name, _count) \
227   VK_MULTIALLOC_DECL_SIZE(_ma, _type, _name, (_count) * sizeof(_type));
228
229static ALWAYS_INLINE void *
230vk_multialloc_alloc(struct vk_multialloc *ma,
231                    const VkAllocationCallbacks *alloc,
232                    VkSystemAllocationScope scope)
233{
234   void *ptr = vk_alloc(alloc, ma->size, ma->align, scope);
235   if (!ptr)
236      return NULL;
237
238   /* Fill out each of the pointers with their final value.
239    *
240    *   for (uint32_t i = 0; i < ma->ptr_count; i++)
241    *      *ma->ptrs[i] = ptr + (uintptr_t)*ma->ptrs[i];
242    *
243    * Unfortunately, even though ma->ptr_count is basically guaranteed to be a
244    * constant, GCC is incapable of figuring this out and unrolling the loop
245    * so we have to give it a little help.
246    */
247   STATIC_ASSERT(ARRAY_SIZE(ma->ptrs) == 12);
248#define _VK_MULTIALLOC_UPDATE_POINTER(_i) \
249   if ((_i) < ma->ptr_count) \
250      *ma->ptrs[_i] = (char *)ptr + (uintptr_t)*ma->ptrs[_i]
251   _VK_MULTIALLOC_UPDATE_POINTER(0);
252   _VK_MULTIALLOC_UPDATE_POINTER(1);
253   _VK_MULTIALLOC_UPDATE_POINTER(2);
254   _VK_MULTIALLOC_UPDATE_POINTER(3);
255   _VK_MULTIALLOC_UPDATE_POINTER(4);
256   _VK_MULTIALLOC_UPDATE_POINTER(5);
257   _VK_MULTIALLOC_UPDATE_POINTER(6);
258   _VK_MULTIALLOC_UPDATE_POINTER(7);
259   _VK_MULTIALLOC_UPDATE_POINTER(8);
260   _VK_MULTIALLOC_UPDATE_POINTER(9);
261   _VK_MULTIALLOC_UPDATE_POINTER(10);
262   _VK_MULTIALLOC_UPDATE_POINTER(11);
263#undef _VK_MULTIALLOC_UPDATE_POINTER
264
265   return ptr;
266}
267
268static ALWAYS_INLINE void *
269vk_multialloc_alloc2(struct vk_multialloc *ma,
270                     const VkAllocationCallbacks *parent_alloc,
271                     const VkAllocationCallbacks *alloc,
272                     VkSystemAllocationScope scope)
273{
274   return vk_multialloc_alloc(ma, alloc ? alloc : parent_alloc, scope);
275}
276
277static ALWAYS_INLINE void *
278vk_multialloc_zalloc(struct vk_multialloc *ma,
279                     const VkAllocationCallbacks *alloc,
280                     VkSystemAllocationScope scope)
281{
282   void *ptr = vk_multialloc_alloc(ma, alloc, scope);
283
284   if (ptr == NULL)
285      return NULL;
286
287   memset(ptr, 0, ma->size);
288
289   return ptr;
290}
291
292static ALWAYS_INLINE void *
293vk_multialloc_zalloc2(struct vk_multialloc *ma,
294                      const VkAllocationCallbacks *parent_alloc,
295                      const VkAllocationCallbacks *alloc,
296                      VkSystemAllocationScope scope)
297{
298   return vk_multialloc_zalloc(ma, alloc ? alloc : parent_alloc, scope);
299}
300
301#ifdef __cplusplus
302}
303#endif
304
305#endif
306