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
37 extern "C" {
38 #endif
39 
40 const VkAllocationCallbacks *
41 vk_default_allocator(void);
42 
43 static inline void *
vk_alloc(const VkAllocationCallbacks * alloc,size_t size,size_t align,VkSystemAllocationScope scope)44 vk_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 
51 static inline void *
vk_zalloc(const VkAllocationCallbacks * alloc,size_t size,size_t align,VkSystemAllocationScope scope)52 vk_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 
65 static inline void *
vk_realloc(const VkAllocationCallbacks * alloc,void * ptr,size_t size,size_t align,VkSystemAllocationScope scope)66 vk_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 
73 static inline void
vk_free(const VkAllocationCallbacks * alloc,void * data)74 vk_free(const VkAllocationCallbacks *alloc, void *data)
75 {
76    if (data == NULL)
77       return;
78 
79    alloc->pfnFree(alloc->pUserData, data);
80 }
81 
82 static inline char *
vk_strdup(const VkAllocationCallbacks * alloc,const char * s,VkSystemAllocationScope scope)83 vk_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 
99 static inline char *
vk_vasprintf(const VkAllocationCallbacks * alloc,VkSystemAllocationScope scope,const char * fmt,va_list args)100 vk_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 
112 PRINTFLIKE(3, 4) static inline char *
vk_asprintf(const VkAllocationCallbacks * alloc,VkSystemAllocationScope scope,const char * fmt,...)113 vk_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 
125 static inline void *
vk_alloc2(const VkAllocationCallbacks * parent_alloc,const VkAllocationCallbacks * alloc,size_t size,size_t align,VkSystemAllocationScope scope)126 vk_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 
137 static inline void *
vk_zalloc2(const VkAllocationCallbacks * parent_alloc,const VkAllocationCallbacks * alloc,size_t size,size_t align,VkSystemAllocationScope scope)138 vk_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 
152 static inline void
vk_free2(const VkAllocationCallbacks * parent_alloc,const VkAllocationCallbacks * alloc,void * data)153 vk_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  */
179 struct vk_multialloc {
180     size_t size;
181     size_t align;
182 
183     uint32_t ptr_count;
184     void **ptrs[8];
185 };
186 
187 #define VK_MULTIALLOC(_name) \
188    struct vk_multialloc _name = { 0, }
189 
190 static ALWAYS_INLINE void
vk_multialloc_add_size_align(struct vk_multialloc * ma,void ** ptr,size_t size,size_t align)191 vk_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 
229 static ALWAYS_INLINE void *
vk_multialloc_alloc(struct vk_multialloc * ma,const VkAllocationCallbacks * alloc,VkSystemAllocationScope scope)230 vk_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) == 8);
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 #undef _VK_MULTIALLOC_UPDATE_POINTER
260 
261    return ptr;
262 }
263 
264 static ALWAYS_INLINE void *
vk_multialloc_alloc2(struct vk_multialloc * ma,const VkAllocationCallbacks * parent_alloc,const VkAllocationCallbacks * alloc,VkSystemAllocationScope scope)265 vk_multialloc_alloc2(struct vk_multialloc *ma,
266                      const VkAllocationCallbacks *parent_alloc,
267                      const VkAllocationCallbacks *alloc,
268                      VkSystemAllocationScope scope)
269 {
270    return vk_multialloc_alloc(ma, alloc ? alloc : parent_alloc, scope);
271 }
272 
273 static ALWAYS_INLINE void *
vk_multialloc_zalloc(struct vk_multialloc * ma,const VkAllocationCallbacks * alloc,VkSystemAllocationScope scope)274 vk_multialloc_zalloc(struct vk_multialloc *ma,
275                      const VkAllocationCallbacks *alloc,
276                      VkSystemAllocationScope scope)
277 {
278    void *ptr = vk_multialloc_alloc(ma, alloc, scope);
279 
280    if (ptr == NULL)
281       return NULL;
282 
283    memset(ptr, 0, ma->size);
284 
285    return ptr;
286 }
287 
288 static ALWAYS_INLINE void *
vk_multialloc_zalloc2(struct vk_multialloc * ma,const VkAllocationCallbacks * parent_alloc,const VkAllocationCallbacks * alloc,VkSystemAllocationScope scope)289 vk_multialloc_zalloc2(struct vk_multialloc *ma,
290                       const VkAllocationCallbacks *parent_alloc,
291                       const VkAllocationCallbacks *alloc,
292                       VkSystemAllocationScope scope)
293 {
294    return vk_multialloc_zalloc(ma, alloc ? alloc : parent_alloc, scope);
295 }
296 
297 #ifdef __cplusplus
298 }
299 #endif
300 
301 #endif
302