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
36const VkAllocationCallbacks *
37vk_default_allocator(void);
38
39static inline void *
40vk_alloc(const VkAllocationCallbacks *alloc,
41         size_t size, size_t align,
42         VkSystemAllocationScope scope)
43{
44   return alloc->pfnAllocation(alloc->pUserData, size, align, scope);
45}
46
47static inline void *
48vk_zalloc(const VkAllocationCallbacks *alloc,
49          size_t size, size_t align,
50          VkSystemAllocationScope scope)
51{
52   void *mem = vk_alloc(alloc, size, align, scope);
53   if (mem == NULL)
54      return NULL;
55
56   memset(mem, 0, size);
57
58   return mem;
59}
60
61static inline void *
62vk_realloc(const VkAllocationCallbacks *alloc,
63           void *ptr, size_t size, size_t align,
64           VkSystemAllocationScope scope)
65{
66   return alloc->pfnReallocation(alloc->pUserData, ptr, size, align, scope);
67}
68
69static inline void
70vk_free(const VkAllocationCallbacks *alloc, void *data)
71{
72   if (data == NULL)
73      return;
74
75   alloc->pfnFree(alloc->pUserData, data);
76}
77
78static inline char *
79vk_strdup(const VkAllocationCallbacks *alloc, const char *s,
80          VkSystemAllocationScope scope)
81{
82   if (s == NULL)
83      return NULL;
84
85   size_t size = strlen(s) + 1;
86   char *copy = (char *)vk_alloc(alloc, size, 1, scope);
87   if (copy == NULL)
88      return NULL;
89
90   memcpy(copy, s, size);
91
92   return copy;
93}
94
95static inline char *
96vk_vasprintf(const VkAllocationCallbacks *alloc,
97             VkSystemAllocationScope scope,
98             const char *fmt, va_list args)
99{
100   size_t size = u_printf_length(fmt, args) + 1;
101   char *ptr = (char *)vk_alloc(alloc, size, 1, scope);
102   if (ptr != NULL)
103      vsnprintf(ptr, size, fmt, args);
104
105   return ptr;
106}
107
108PRINTFLIKE(3, 4) static inline char *
109vk_asprintf(const VkAllocationCallbacks *alloc,
110            VkSystemAllocationScope scope,
111            const char *fmt, ...)
112{
113   va_list args;
114   va_start(args, fmt);
115   char *ptr = vk_vasprintf(alloc, scope, fmt, args);
116   va_end(args);
117
118   return ptr;
119}
120
121static inline void *
122vk_alloc2(const VkAllocationCallbacks *parent_alloc,
123          const VkAllocationCallbacks *alloc,
124          size_t size, size_t align,
125          VkSystemAllocationScope scope)
126{
127   if (alloc)
128      return vk_alloc(alloc, size, align, scope);
129   else
130      return vk_alloc(parent_alloc, size, align, scope);
131}
132
133static inline void *
134vk_zalloc2(const VkAllocationCallbacks *parent_alloc,
135           const VkAllocationCallbacks *alloc,
136           size_t size, size_t align,
137           VkSystemAllocationScope scope)
138{
139   void *mem = vk_alloc2(parent_alloc, alloc, size, align, scope);
140   if (mem == NULL)
141      return NULL;
142
143   memset(mem, 0, size);
144
145   return mem;
146}
147
148static inline void
149vk_free2(const VkAllocationCallbacks *parent_alloc,
150         const VkAllocationCallbacks *alloc,
151         void *data)
152{
153   if (alloc)
154      vk_free(alloc, data);
155   else
156      vk_free(parent_alloc, data);
157}
158
159/* A multi-pointer allocator
160 *
161 * When copying data structures from the user (such as a render pass), it's
162 * common to need to allocate data for a bunch of different things.  Instead
163 * of doing several allocations and having to handle all of the error checking
164 * that entails, it can be easier to do a single allocation.  This struct
165 * helps facilitate that.  The intended usage looks like this:
166 *
167 *    VK_MULTIALLOC(ma)
168 *    vk_multialloc_add(&ma, &main_ptr, 1);
169 *    vk_multialloc_add(&ma, &substruct1, substruct1Count);
170 *    vk_multialloc_add(&ma, &substruct2, substruct2Count);
171 *
172 *    if (!vk_multialloc_alloc(&ma, pAllocator, VK_ALLOCATION_SCOPE_FOO))
173 *       return vk_error(VK_ERROR_OUT_OF_HOST_MEORY);
174 */
175struct vk_multialloc {
176    size_t size;
177    size_t align;
178
179    uint32_t ptr_count;
180    void **ptrs[8];
181};
182
183#define VK_MULTIALLOC_INIT \
184   ((struct vk_multialloc) { 0, })
185
186#define VK_MULTIALLOC(_name) \
187   struct vk_multialloc _name = VK_MULTIALLOC_INIT
188
189static ALWAYS_INLINE void
190vk_multialloc_add_size_align(struct vk_multialloc *ma,
191                             void **ptr, size_t size, size_t align)
192{
193   assert(util_is_power_of_two_nonzero(align));
194   if (size == 0) {
195      *ptr = NULL;
196      return;
197   }
198
199   size_t offset = ALIGN_POT(ma->size, align);
200   ma->size = offset + size;
201   ma->align = MAX2(ma->align, align);
202
203   /* Store the offset in the pointer. */
204   *ptr = (void *)(uintptr_t)offset;
205
206   assert(ma->ptr_count < ARRAY_SIZE(ma->ptrs));
207   ma->ptrs[ma->ptr_count++] = ptr;
208}
209
210#define vk_multialloc_add_size(_ma, _ptr, _type, _size) \
211   do { \
212      _type **_tmp = (_ptr); \
213      (void)_tmp; \
214      vk_multialloc_add_size_align((_ma), (void **)(_ptr), \
215                                   (_size), alignof(_type)); \
216   } while(0)
217
218#define vk_multialloc_add(_ma, _ptr, _type, _count) \
219   vk_multialloc_add_size(_ma, _ptr, _type, (_count) * sizeof(**(_ptr)));
220
221#define VK_MULTIALLOC_DECL_SIZE(_ma, _type, _name, _size) \
222   _type *_name; \
223   vk_multialloc_add_size(_ma, &_name, _type, _size);
224
225#define VK_MULTIALLOC_DECL(_ma, _type, _name, _count) \
226   VK_MULTIALLOC_DECL_SIZE(_ma, _type, _name, (_count) * sizeof(_type));
227
228static ALWAYS_INLINE void *
229vk_multialloc_alloc(struct vk_multialloc *ma,
230                    const VkAllocationCallbacks *alloc,
231                    VkSystemAllocationScope scope)
232{
233   char *ptr = (char *)vk_alloc(alloc, ma->size, ma->align, scope);
234   if (!ptr)
235      return NULL;
236
237   /* Fill out each of the pointers with their final value.
238    *
239    *   for (uint32_t i = 0; i < ma->ptr_count; i++)
240    *      *ma->ptrs[i] = ptr + (uintptr_t)*ma->ptrs[i];
241    *
242    * Unfortunately, even though ma->ptr_count is basically guaranteed to be a
243    * constant, GCC is incapable of figuring this out and unrolling the loop
244    * so we have to give it a little help.
245    */
246   STATIC_ASSERT(ARRAY_SIZE(ma->ptrs) == 8);
247#define _VK_MULTIALLOC_UPDATE_POINTER(_i) \
248   if ((_i) < ma->ptr_count) \
249      *ma->ptrs[_i] = ptr + (uintptr_t)*ma->ptrs[_i]
250   _VK_MULTIALLOC_UPDATE_POINTER(0);
251   _VK_MULTIALLOC_UPDATE_POINTER(1);
252   _VK_MULTIALLOC_UPDATE_POINTER(2);
253   _VK_MULTIALLOC_UPDATE_POINTER(3);
254   _VK_MULTIALLOC_UPDATE_POINTER(4);
255   _VK_MULTIALLOC_UPDATE_POINTER(5);
256   _VK_MULTIALLOC_UPDATE_POINTER(6);
257   _VK_MULTIALLOC_UPDATE_POINTER(7);
258#undef _VK_MULTIALLOC_UPDATE_POINTER
259
260   return ptr;
261}
262
263static ALWAYS_INLINE void *
264vk_multialloc_alloc2(struct vk_multialloc *ma,
265                     const VkAllocationCallbacks *parent_alloc,
266                     const VkAllocationCallbacks *alloc,
267                     VkSystemAllocationScope scope)
268{
269   return vk_multialloc_alloc(ma, alloc ? alloc : parent_alloc, scope);
270}
271
272static ALWAYS_INLINE void *
273vk_multialloc_zalloc(struct vk_multialloc *ma,
274                     const VkAllocationCallbacks *alloc,
275                     VkSystemAllocationScope scope)
276{
277   void *ptr = vk_multialloc_alloc(ma, alloc, scope);
278
279   if (ptr == NULL)
280      return NULL;
281
282   memset(ptr, 0, ma->size);
283
284   return ptr;
285}
286
287static ALWAYS_INLINE void *
288vk_multialloc_zalloc2(struct vk_multialloc *ma,
289                      const VkAllocationCallbacks *parent_alloc,
290                      const VkAllocationCallbacks *alloc,
291                      VkSystemAllocationScope scope)
292{
293   return vk_multialloc_zalloc(ma, alloc ? alloc : parent_alloc, scope);
294}
295
296#endif
297