Branch data Line data Source code
1 : : /*
2 : : * This file is part of libplacebo.
3 : : *
4 : : * libplacebo is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * libplacebo is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : : * GNU Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #include "malloc.h"
19 : : #include "command.h"
20 : : #include "utils.h"
21 : : #include "pl_thread.h"
22 : :
23 : : #ifdef PL_HAVE_UNIX
24 : : #include <errno.h>
25 : : #include <unistd.h>
26 : : #endif
27 : :
28 : : // Controls the page size alignment, to help coalesce allocations into the same
29 : : // slab. Pages are rounded up to multiples of this value. (Default: 4 KB)
30 : : #define PAGE_SIZE_ALIGN (1LLU << 12)
31 : :
32 : : // Controls the minimum/maximum number of pages for new slabs. As slabs are
33 : : // exhausted of memory, the number of pages per new slab grows exponentially,
34 : : // starting with the minimum until the maximum is reached.
35 : : //
36 : : // Note: The maximum must never exceed the size of `vk_slab.spacemap`.
37 : : #define MINIMUM_PAGE_COUNT 4
38 : : #define MAXIMUM_PAGE_COUNT (sizeof(uint64_t) * 8)
39 : :
40 : : // Controls the maximum page size. Any allocations above this threshold
41 : : // (absolute size or fraction of VRAM, whichever is higher) will be served by
42 : : // dedicated allocations. (Default: 64 MB or 1/16 of VRAM)
43 : : #define MAXIMUM_PAGE_SIZE_ABSOLUTE (1LLU << 26)
44 : : #define MAXIMUM_PAGE_SIZE_RELATIVE 16
45 : :
46 : : // Controls the minimum slab size, to avoid excessive re-allocation of very
47 : : // small slabs. (Default: 256 KB)
48 : : #define MINIMUM_SLAB_SIZE (1LLU << 18)
49 : :
50 : : // How long to wait before garbage collecting empty slabs. Slabs older than
51 : : // this many invocations of `vk_malloc_garbage_collect` will be released.
52 : : #define MAXIMUM_SLAB_AGE 32
53 : :
54 : : // A single slab represents a contiguous region of allocated memory. Actual
55 : : // allocations are served as pages of this. Slabs are organized into pools,
56 : : // each of which contains a list of slabs of differing page sizes.
57 : : struct vk_slab {
58 : : pl_mutex lock;
59 : : pl_debug_tag debug_tag; // debug tag of the triggering allocation
60 : : VkDeviceMemory mem; // underlying device allocation
61 : : VkDeviceSize size; // total allocated size of `mem`
62 : : VkMemoryType mtype; // underlying memory type
63 : : bool dedicated; // slab is allocated specifically for one object
64 : : bool imported; // slab represents an imported memory allocation
65 : :
66 : : // free space accounting (only for non-dedicated slabs)
67 : : uint64_t spacemap; // bitset of available pages
68 : : size_t pagesize; // size in bytes per page
69 : : size_t used; // number of bytes actually in use
70 : : uint64_t age; // timestamp of last use
71 : :
72 : : // optional, depends on the memory type:
73 : : VkBuffer buffer; // buffer spanning the entire slab
74 : : void *data; // mapped memory corresponding to `mem`
75 : : bool coherent; // mapped memory is coherent
76 : : union pl_handle handle; // handle associated with this device memory
77 : : enum pl_handle_type handle_type;
78 : : };
79 : :
80 : : // Represents a single memory pool. We keep track of a vk_pool for each
81 : : // combination of malloc parameters. This shouldn't actually be that many in
82 : : // practice, because some combinations simply never occur, and others will
83 : : // generally be the same for the same objects.
84 : : //
85 : : // Note: `vk_pool` addresses are not immutable, so we mustn't expose any
86 : : // dangling references to a `vk_pool` from e.g. `vk_memslice.priv = vk_slab`.
87 : : struct vk_pool {
88 : : struct vk_malloc_params params; // allocation params (with some fields nulled)
89 : : PL_ARRAY(struct vk_slab *) slabs; // array of slabs, unsorted
90 : : int index; // running index in `vk_malloc.pools`
91 : : };
92 : :
93 : : // The overall state of the allocator, which keeps track of a vk_pool for each
94 : : // memory type.
95 : : struct vk_malloc {
96 : : struct vk_ctx *vk;
97 : : pl_mutex lock;
98 : : VkPhysicalDeviceMemoryProperties props;
99 : : size_t maximum_page_size;
100 : : PL_ARRAY(struct vk_pool) pools;
101 : : uint64_t age;
102 : : };
103 : :
104 : : static inline float efficiency(size_t used, size_t total)
105 : : {
106 [ + + + + ]: 41 : if (!total)
107 : : return 100.0;
108 : :
109 : 25 : return 100.0f * used / total;
110 : : }
111 : :
112 : 423 : static const char *print_size(char buf[8], size_t size)
113 : : {
114 : : const char *suffixes = "\0KMG";
115 [ + - + + ]: 673 : while (suffixes[1] && size > 9999) {
116 : 250 : size >>= 10;
117 : 250 : suffixes++;
118 : : }
119 : :
120 : 217 : int ret = *suffixes ? snprintf(buf, 8, "%4zu%c", size, *suffixes)
121 [ + + ]: 423 : : snprintf(buf, 8, "%5zu", size);
122 : :
123 [ - + ]: 423 : return ret >= 0 ? buf : "(error)";
124 : : }
125 : :
126 : : #define PRINT_SIZE(x) (print_size((char[8]){0}, (size_t) (x)))
127 : :
128 : 7 : void vk_malloc_print_stats(struct vk_malloc *ma, enum pl_log_level lev)
129 : : {
130 : 7 : struct vk_ctx *vk = ma->vk;
131 : : size_t total_size = 0;
132 : : size_t total_used = 0;
133 : : size_t total_res = 0;
134 : :
135 : 7 : PL_MSG(vk, lev, "Memory heaps supported by device:");
136 [ + + ]: 14 : for (int i = 0; i < ma->props.memoryHeapCount; i++) {
137 : 7 : VkMemoryHeap heap = ma->props.memoryHeaps[i];
138 : 7 : PL_MSG(vk, lev, " %d: flags 0x%x size %s",
139 : : i, (unsigned) heap.flags, PRINT_SIZE(heap.size));
140 : : }
141 : :
142 : 7 : PL_DEBUG(vk, "Memory types supported by device:");
143 [ + + ]: 35 : for (int i = 0; i < ma->props.memoryTypeCount; i++) {
144 : 28 : VkMemoryType type = ma->props.memoryTypes[i];
145 : 28 : PL_DEBUG(vk, " %d: flags 0x%x heap %d",
146 : : i, (unsigned) type.propertyFlags, (int) type.heapIndex);
147 : : }
148 : :
149 : 7 : pl_mutex_lock(&ma->lock);
150 [ + + ]: 41 : for (int i = 0; i < ma->pools.num; i++) {
151 : 34 : struct vk_pool *pool = &ma->pools.elem[i];
152 : : const struct vk_malloc_params *par = &pool->params;
153 : :
154 : 34 : PL_MSG(vk, lev, "Memory pool %d:", i);
155 : 34 : PL_MSG(vk, lev, " Compatible types: 0x%"PRIx32, par->reqs.memoryTypeBits);
156 [ + + ]: 34 : if (par->required)
157 : 22 : PL_MSG(vk, lev, " Required flags: 0x%"PRIx32, par->required);
158 [ + + ]: 34 : if (par->optimal)
159 : 26 : PL_MSG(vk, lev, " Optimal flags: 0x%"PRIx32, par->optimal);
160 [ + + ]: 34 : if (par->buf_usage)
161 : 28 : PL_MSG(vk, lev, " Buffer flags: 0x%"PRIx32, par->buf_usage);
162 [ + + ]: 34 : if (par->export_handle)
163 : 8 : PL_MSG(vk, lev, " Export handle: 0x%x", par->export_handle);
164 : :
165 : : size_t pool_size = 0;
166 : : size_t pool_used = 0;
167 : : size_t pool_res = 0;
168 : :
169 [ + + ]: 78 : for (int j = 0; j < pool->slabs.num; j++) {
170 : 44 : struct vk_slab *slab = pool->slabs.elem[j];
171 : 44 : pl_mutex_lock(&slab->lock);
172 : :
173 : 44 : size_t avail = __builtin_popcountll(slab->spacemap) * slab->pagesize;
174 : 44 : size_t slab_res = slab->size - avail;
175 : :
176 [ + - - + ]: 88 : PL_MSG(vk, lev, " Slab %2d: %8"PRIx64" x %s: "
177 : : "%s used %s res %s alloc from heap %d, efficiency %.2f%% [%s]",
178 : : j, slab->spacemap, PRINT_SIZE(slab->pagesize),
179 : : PRINT_SIZE(slab->used), PRINT_SIZE(slab_res),
180 : : PRINT_SIZE(slab->size), (int) slab->mtype.heapIndex,
181 : : efficiency(slab->used, slab_res),
182 : : PL_DEF(slab->debug_tag, "unknown"));
183 : :
184 : 44 : pool_size += slab->size;
185 : 44 : pool_used += slab->used;
186 : 44 : pool_res += slab_res;
187 : 44 : pl_mutex_unlock(&slab->lock);
188 : : }
189 : :
190 [ - + ]: 34 : PL_MSG(vk, lev, " Pool summary: %s used %s res %s alloc, "
191 : : "efficiency %.2f%%, utilization %.2f%%",
192 : : PRINT_SIZE(pool_used), PRINT_SIZE(pool_res),
193 : : PRINT_SIZE(pool_size), efficiency(pool_used, pool_res),
194 : : efficiency(pool_res, pool_size));
195 : :
196 : 34 : total_size += pool_size;
197 : 34 : total_used += pool_used;
198 : 34 : total_res += pool_res;
199 : : }
200 : 7 : pl_mutex_unlock(&ma->lock);
201 : :
202 [ - + ]: 14 : PL_MSG(vk, lev, "Memory summary: %s used %s res %s alloc, "
203 : : "efficiency %.2f%%, utilization %.2f%%, max page: %s",
204 : : PRINT_SIZE(total_used), PRINT_SIZE(total_res),
205 : : PRINT_SIZE(total_size), efficiency(total_used, total_res),
206 : : efficiency(total_res, total_size),
207 : : PRINT_SIZE(ma->maximum_page_size));
208 : 7 : }
209 : :
210 : 181 : static void slab_free(struct vk_ctx *vk, struct vk_slab *slab)
211 : : {
212 [ + + ]: 181 : if (!slab)
213 : : return;
214 : :
215 : : #ifndef NDEBUG
216 [ + + - + ]: 68 : if (!slab->dedicated && slab->used > 0) {
217 : 0 : PL_WARN(vk, "Leaked %zu bytes of vulkan memory!", slab->used);
218 : 0 : PL_WARN(vk, "slab total size: %zu bytes, heap: %d, flags: 0x%"PRIX64,
219 : : (size_t) slab->size, (int) slab->mtype.heapIndex,
220 : : (uint64_t) slab->mtype.propertyFlags);
221 [ # # ]: 0 : if (slab->debug_tag)
222 : 0 : PL_WARN(vk, "last used for: %s", slab->debug_tag);
223 : 0 : pl_log_stack_trace(vk->log, PL_LOG_WARN);
224 : 0 : pl_debug_abort();
225 : : }
226 : : #endif
227 : :
228 [ + + ]: 68 : if (slab->imported) {
229 [ + + - - : 20 : switch (slab->handle_type) {
- ]
230 : 4 : case PL_HANDLE_FD:
231 : : case PL_HANDLE_DMA_BUF:
232 : 4 : PL_TRACE(vk, "Unimporting slab of size %s from fd: %d",
233 : : PRINT_SIZE(slab->size), slab->handle.fd);
234 : 4 : break;
235 : : case PL_HANDLE_WIN32:
236 : : case PL_HANDLE_WIN32_KMT:
237 : : #ifdef PL_HAVE_WIN32
238 : : PL_TRACE(vk, "Unimporting slab of size %s from handle: %p",
239 : : PRINT_SIZE(slab->size), (void *) slab->handle.handle);
240 : : #endif
241 : : break;
242 : 16 : case PL_HANDLE_HOST_PTR:
243 : 16 : PL_TRACE(vk, "Unimporting slab of size %s from ptr: %p",
244 : : PRINT_SIZE(slab->size), (void *) slab->handle.ptr);
245 : 16 : break;
246 : : case PL_HANDLE_IOSURFACE:
247 : : case PL_HANDLE_MTL_TEX:
248 : 0 : pl_unreachable();
249 : : }
250 : : } else {
251 [ + - + ]: 48 : switch (slab->handle_type) {
252 : 8 : case PL_HANDLE_FD:
253 : : case PL_HANDLE_DMA_BUF:
254 : : #ifdef PL_HAVE_UNIX
255 [ + - ]: 8 : if (slab->handle.fd > -1)
256 : 8 : close(slab->handle.fd);
257 : : #endif
258 : : break;
259 : : case PL_HANDLE_WIN32:
260 : : #ifdef PL_HAVE_WIN32
261 : : if (slab->handle.handle != NULL)
262 : : CloseHandle(slab->handle.handle);
263 : : #endif
264 : : break;
265 : : case PL_HANDLE_WIN32_KMT:
266 : : // PL_HANDLE_WIN32_KMT is just an identifier. It doesn't get closed.
267 : : break;
268 : : case PL_HANDLE_HOST_PTR:
269 : : // Implicitly unmapped
270 : : break;
271 : : case PL_HANDLE_IOSURFACE:
272 : : case PL_HANDLE_MTL_TEX:
273 : 0 : pl_unreachable();
274 : : }
275 : :
276 : 48 : PL_DEBUG(vk, "Freeing slab of size %s", PRINT_SIZE(slab->size));
277 : : }
278 : :
279 : 68 : vk->DestroyBuffer(vk->dev, slab->buffer, PL_VK_ALLOC);
280 : : // also implicitly unmaps the memory if needed
281 : 68 : vk->FreeMemory(vk->dev, slab->mem, PL_VK_ALLOC);
282 : :
283 : 68 : pl_mutex_destroy(&slab->lock);
284 : 68 : pl_free(slab);
285 : : }
286 : :
287 : : // type_mask: optional
288 : : // thread-safety: safe
289 : 68 : static bool find_best_memtype(const struct vk_malloc *ma, uint32_t type_mask,
290 : : const struct vk_malloc_params *params,
291 : : uint32_t *out_index)
292 : : {
293 : 68 : struct vk_ctx *vk = ma->vk;
294 : : int best = -1;
295 : :
296 : : // The vulkan spec requires memory types to be sorted in the "optimal"
297 : : // order, so the first matching type we find will be the best/fastest one.
298 : : // That being said, we still want to prioritize memory types that have
299 : : // better optional flags.
300 : :
301 : 68 : type_mask &= params->reqs.memoryTypeBits;
302 [ + + ]: 340 : for (int i = 0; i < ma->props.memoryTypeCount; i++) {
303 : : const VkMemoryType *mtype = &ma->props.memoryTypes[i];
304 : :
305 : : // The memory type flags must include our properties
306 [ - + ]: 272 : if ((mtype->propertyFlags & params->required) != params->required)
307 : 0 : continue;
308 : :
309 : : // The memory heap must be large enough for the allocation
310 : 272 : VkDeviceSize heapSize = ma->props.memoryHeaps[mtype->heapIndex].size;
311 [ - + ]: 272 : if (params->reqs.size > heapSize)
312 : 0 : continue;
313 : :
314 : : // The memory type must be supported by the type mask (bitfield)
315 [ + + ]: 272 : if (!(type_mask & (1LU << i)))
316 : 136 : continue;
317 : :
318 : : // Calculate the score as the number of optimal property flags matched
319 : 136 : int score = __builtin_popcountl(mtype->propertyFlags & params->optimal);
320 [ + + ]: 136 : if (score > best) {
321 : 78 : *out_index = i;
322 : : best = score;
323 : : }
324 : : }
325 : :
326 [ - + ]: 68 : if (best < 0) {
327 : 0 : PL_ERR(vk, "Found no memory type matching property flags 0x%x and type "
328 : : "bits 0x%x!",
329 : : (unsigned) params->required, (unsigned) type_mask);
330 : 0 : return false;
331 : : }
332 : :
333 : : return true;
334 : : }
335 : :
336 : 50 : static bool buf_external_check(struct vk_ctx *vk, VkBufferUsageFlags usage,
337 : : enum pl_handle_type handle_type, bool import)
338 : : {
339 [ + + ]: 50 : if (!handle_type)
340 : : return true;
341 : :
342 : 44 : VkPhysicalDeviceExternalBufferInfo info = {
343 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR,
344 : : .usage = usage,
345 : 22 : .handleType = vk_mem_handle_type(handle_type),
346 : : };
347 : :
348 : 22 : VkExternalBufferProperties props = {
349 : : .sType = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR,
350 : : };
351 : :
352 [ + - ]: 22 : if (!info.handleType)
353 : : return false;
354 : :
355 : 22 : vk->GetPhysicalDeviceExternalBufferProperties(vk->physd, &info, &props);
356 : 22 : return vk_external_mem_check(vk, &props.externalMemoryProperties,
357 : : handle_type, import);
358 : : }
359 : :
360 : : // thread-safety: safe
361 : 48 : static struct vk_slab *slab_alloc(struct vk_malloc *ma,
362 : : const struct vk_malloc_params *params)
363 : : {
364 : 48 : struct vk_ctx *vk = ma->vk;
365 : 48 : struct vk_slab *slab = pl_alloc_ptr(NULL, slab);
366 : 48 : *slab = (struct vk_slab) {
367 : 48 : .age = ma->age,
368 : 48 : .size = params->reqs.size,
369 : 48 : .handle_type = params->export_handle,
370 : 48 : .debug_tag = params->debug_tag,
371 : : };
372 [ - + ]: 48 : pl_mutex_init(&slab->lock);
373 : :
374 [ + - - + ]: 48 : switch (slab->handle_type) {
375 : 8 : case PL_HANDLE_FD:
376 : : case PL_HANDLE_DMA_BUF:
377 : 8 : slab->handle.fd = -1;
378 : 8 : break;
379 : 0 : case PL_HANDLE_WIN32:
380 : : case PL_HANDLE_WIN32_KMT:
381 : : case PL_HANDLE_MTL_TEX:
382 : : case PL_HANDLE_IOSURFACE:
383 : 0 : slab->handle.handle = NULL;
384 : 0 : break;
385 : 0 : case PL_HANDLE_HOST_PTR:
386 : 0 : slab->handle.ptr = NULL;
387 : 0 : break;
388 : : }
389 : :
390 : 96 : VkExportMemoryAllocateInfoKHR ext_info = {
391 : : .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR,
392 : 48 : .handleTypes = vk_mem_handle_type(slab->handle_type),
393 : : };
394 : :
395 : : uint32_t type_mask = UINT32_MAX;
396 [ + + ]: 48 : if (params->buf_usage) {
397 : : // Queue family sharing modes don't matter for buffers, so we just
398 : : // set them as concurrent and stop worrying about it.
399 : 32 : uint32_t qfs[3] = {0};
400 [ + - ]: 32 : pl_assert(vk->pools.num <= PL_ARRAY_SIZE(qfs));
401 [ + + ]: 64 : for (int i = 0; i < vk->pools.num; i++)
402 : 32 : qfs[i] = vk->pools.elem[i]->qf;
403 : :
404 : 32 : VkExternalMemoryBufferCreateInfoKHR ext_buf_info = {
405 : : .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
406 : : .handleTypes = ext_info.handleTypes,
407 : : };
408 : :
409 : 64 : VkBufferCreateInfo binfo = {
410 : : .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
411 [ + + ]: 32 : .pNext = slab->handle_type ? &ext_buf_info : NULL,
412 : 32 : .size = slab->size,
413 : : .usage = params->buf_usage,
414 : : .sharingMode = vk->pools.num > 1 ? VK_SHARING_MODE_CONCURRENT
415 : 32 : : VK_SHARING_MODE_EXCLUSIVE,
416 : : .queueFamilyIndexCount = vk->pools.num,
417 : : .pQueueFamilyIndices = qfs,
418 : : };
419 : :
420 [ - + ]: 32 : if (!buf_external_check(vk, binfo.usage, slab->handle_type, false)) {
421 : 0 : PL_ERR(vk, "Failed allocating shared memory buffer: possibly "
422 : : "the handle type is unsupported?");
423 : 0 : goto error;
424 : : }
425 : :
426 [ - + ]: 32 : VK(vk->CreateBuffer(vk->dev, &binfo, PL_VK_ALLOC, &slab->buffer));
427 [ + - ]: 32 : PL_VK_NAME(BUFFER, slab->buffer, "slab");
428 : :
429 : 32 : VkMemoryRequirements reqs = {0};
430 : 32 : vk->GetBufferMemoryRequirements(vk->dev, slab->buffer, &reqs);
431 : 32 : slab->size = reqs.size; // this can be larger than `slab->size`
432 : 32 : type_mask = reqs.memoryTypeBits;
433 : :
434 : : // Note: we can ignore `reqs.align` because we always bind the buffer
435 : : // memory to offset 0
436 : : }
437 : :
438 : 48 : VkMemoryAllocateInfo minfo = {
439 : : .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
440 : 48 : .allocationSize = slab->size,
441 : : };
442 : :
443 [ + + ]: 48 : if (params->export_handle)
444 : 8 : vk_link_struct(&minfo, &ext_info);
445 : :
446 : 48 : VkMemoryDedicatedAllocateInfoKHR dinfo = {
447 : : .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
448 : 48 : .image = params->ded_image,
449 : : };
450 : :
451 [ - + ]: 48 : if (params->ded_image)
452 : 0 : vk_link_struct(&minfo, &dinfo);
453 : :
454 [ - + ]: 48 : if (!find_best_memtype(ma, type_mask, params, &minfo.memoryTypeIndex))
455 : 0 : goto error;
456 : :
457 : 48 : const VkMemoryType *mtype = &ma->props.memoryTypes[minfo.memoryTypeIndex];
458 [ + - ]: 96 : PL_DEBUG(vk, "Allocating %zu memory of type 0x%x (id %d) in heap %d: %s",
459 : : (size_t) slab->size, (unsigned) mtype->propertyFlags,
460 : : (int) minfo.memoryTypeIndex, (int) mtype->heapIndex,
461 : : PL_DEF(params->debug_tag, "unknown"));
462 : :
463 : : pl_clock_t start = pl_clock_now();
464 : :
465 : 48 : VkResult res = vk->AllocateMemory(vk->dev, &minfo, PL_VK_ALLOC, &slab->mem);
466 [ - + ]: 48 : switch (res) {
467 : 0 : case VK_ERROR_OUT_OF_DEVICE_MEMORY:
468 : : case VK_ERROR_OUT_OF_HOST_MEMORY:
469 : 0 : PL_ERR(vk, "Allocation of size %s failed: %s!",
470 : : PRINT_SIZE(slab->size), vk_res_str(res));
471 : 0 : vk_malloc_print_stats(ma, PL_LOG_ERR);
472 : 0 : pl_log_stack_trace(vk->log, PL_LOG_ERR);
473 : 0 : pl_debug_abort();
474 : 0 : goto error;
475 : :
476 : 48 : default:
477 [ - + ]: 48 : PL_VK_ASSERT(res, "vkAllocateMemory");
478 : : }
479 : :
480 : 48 : slab->mtype = *mtype;
481 [ + - ]: 48 : if (mtype->propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
482 [ - + ]: 48 : VK(vk->MapMemory(vk->dev, slab->mem, 0, VK_WHOLE_SIZE, 0, &slab->data));
483 : 48 : slab->coherent = mtype->propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
484 : : }
485 : :
486 [ + + ]: 48 : if (slab->buffer)
487 [ - + ]: 32 : VK(vk->BindBufferMemory(vk->dev, slab->buffer, slab->mem, 0));
488 : :
489 : : #ifdef PL_HAVE_UNIX
490 [ + + ]: 48 : if (slab->handle_type == PL_HANDLE_FD ||
491 : : slab->handle_type == PL_HANDLE_DMA_BUF)
492 : : {
493 : 8 : VkMemoryGetFdInfoKHR fd_info = {
494 : : .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
495 : 8 : .memory = slab->mem,
496 : 8 : .handleType = ext_info.handleTypes,
497 : : };
498 : :
499 [ - + ]: 8 : VK(vk->GetMemoryFdKHR(vk->dev, &fd_info, &slab->handle.fd));
500 : : }
501 : : #endif
502 : :
503 : : #ifdef PL_HAVE_WIN32
504 : : if (slab->handle_type == PL_HANDLE_WIN32 ||
505 : : slab->handle_type == PL_HANDLE_WIN32_KMT)
506 : : {
507 : : VkMemoryGetWin32HandleInfoKHR handle_info = {
508 : : .sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR,
509 : : .memory = slab->mem,
510 : : .handleType = ext_info.handleTypes,
511 : : };
512 : :
513 : : VK(vk->GetMemoryWin32HandleKHR(vk->dev, &handle_info,
514 : : &slab->handle.handle));
515 : : }
516 : : #endif
517 : :
518 : 48 : pl_log_cpu_time(vk->log, start, pl_clock_now(), "allocating slab");
519 : :
520 : : // free space accounting is done by the caller
521 : 48 : return slab;
522 : :
523 : 0 : error:
524 [ # # ]: 0 : if (params->debug_tag)
525 : 0 : PL_ERR(vk, " for malloc: %s", params->debug_tag);
526 : 0 : slab_free(vk, slab);
527 : 0 : return NULL;
528 : : }
529 : :
530 : 21 : static void pool_uninit(struct vk_ctx *vk, struct vk_pool *pool)
531 : : {
532 [ + + ]: 47 : for (int i = 0; i < pool->slabs.num; i++)
533 : 26 : slab_free(vk, pool->slabs.elem[i]);
534 : :
535 : 21 : pl_free(pool->slabs.elem);
536 : 21 : *pool = (struct vk_pool) {0};
537 : 21 : }
538 : :
539 : 3 : struct vk_malloc *vk_malloc_create(struct vk_ctx *vk)
540 : : {
541 : 3 : struct vk_malloc *ma = pl_zalloc_ptr(NULL, ma);
542 [ - + ]: 3 : pl_mutex_init(&ma->lock);
543 : 3 : vk->GetPhysicalDeviceMemoryProperties(vk->physd, &ma->props);
544 : 3 : ma->vk = vk;
545 : :
546 : : // Determine maximum page size
547 : 3 : ma->maximum_page_size = MAXIMUM_PAGE_SIZE_ABSOLUTE;
548 [ + + ]: 6 : for (int i = 0; i < ma->props.memoryHeapCount; i++) {
549 : 3 : VkMemoryHeap heap = ma->props.memoryHeaps[i];
550 [ + - ]: 3 : if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
551 : 3 : size_t size_max = heap.size / MAXIMUM_PAGE_SIZE_RELATIVE;
552 : 3 : ma->maximum_page_size = PL_MAX(ma->maximum_page_size, size_max);
553 : : }
554 : : }
555 : :
556 : 3 : vk_malloc_print_stats(ma, PL_LOG_INFO);
557 : 3 : return ma;
558 : : }
559 : :
560 : 3 : void vk_malloc_destroy(struct vk_malloc **ma_ptr)
561 : : {
562 : 3 : struct vk_malloc *ma = *ma_ptr;
563 [ + - ]: 3 : if (!ma)
564 : : return;
565 : :
566 : 3 : vk_malloc_print_stats(ma, PL_LOG_DEBUG);
567 [ + + ]: 24 : for (int i = 0; i < ma->pools.num; i++)
568 : 21 : pool_uninit(ma->vk, &ma->pools.elem[i]);
569 : :
570 : 3 : pl_mutex_destroy(&ma->lock);
571 : 3 : pl_free_ptr(ma_ptr);
572 : : }
573 : :
574 : 101 : void vk_malloc_garbage_collect(struct vk_malloc *ma)
575 : : {
576 : 101 : struct vk_ctx *vk = ma->vk;
577 : :
578 : 101 : pl_mutex_lock(&ma->lock);
579 : 101 : ma->age++;
580 : :
581 [ + + ]: 1324 : for (int i = 0; i < ma->pools.num; i++) {
582 : 1223 : struct vk_pool *pool = &ma->pools.elem[i];
583 [ + + ]: 2838 : for (int n = 0; n < pool->slabs.num; n++) {
584 : 1615 : struct vk_slab *slab = pool->slabs.elem[n];
585 : 1615 : pl_mutex_lock(&slab->lock);
586 [ + + + + ]: 1615 : if (slab->used || (ma->age - slab->age) <= MAXIMUM_SLAB_AGE) {
587 : 1593 : pl_mutex_unlock(&slab->lock);
588 : 1593 : continue;
589 : : }
590 : :
591 : 22 : PL_DEBUG(vk, "Garbage collected slab of size %s from pool %d",
592 : : PRINT_SIZE(slab->size), pool->index);
593 : :
594 : 22 : pl_mutex_unlock(&slab->lock);
595 : 22 : slab_free(ma->vk, slab);
596 [ - + - - : 22 : PL_ARRAY_REMOVE_AT(pool->slabs, n--);
- + ]
597 : : }
598 : : }
599 : :
600 : 101 : pl_mutex_unlock(&ma->lock);
601 : 101 : }
602 : :
603 : 6 : pl_handle_caps vk_malloc_handle_caps(const struct vk_malloc *ma, bool import)
604 : : {
605 : 6 : struct vk_ctx *vk = ma->vk;
606 : : pl_handle_caps caps = 0;
607 : :
608 [ + + ]: 24 : for (int i = 0; vk_mem_handle_list[i]; i++) {
609 : : // Try seeing if we could allocate a "basic" buffer using these
610 : : // capabilities, with no fancy buffer usage. More specific checks will
611 : : // happen down the line at VkBuffer creation time, but this should give
612 : : // us a rough idea of what the driver supports.
613 : : enum pl_handle_type type = vk_mem_handle_list[i];
614 [ + + ]: 18 : if (buf_external_check(vk, VK_BUFFER_USAGE_TRANSFER_DST_BIT, type, import))
615 : 15 : caps |= type;
616 : : }
617 : :
618 : 6 : return caps;
619 : : }
620 : :
621 : 1642 : void vk_malloc_free(struct vk_malloc *ma, struct vk_memslice *slice)
622 : : {
623 : 1642 : struct vk_ctx *vk = ma->vk;
624 : 1642 : struct vk_slab *slab = slice->priv;
625 [ + + + + ]: 1642 : if (!slab || slab->dedicated) {
626 : 133 : slab_free(vk, slab);
627 : 133 : goto done;
628 : : }
629 : :
630 : 1509 : pl_mutex_lock(&slab->lock);
631 : :
632 : 1509 : int page_idx = slice->offset / slab->pagesize;
633 : 1509 : slab->spacemap |= 0x1LLU << page_idx;
634 : 1509 : slab->used -= slice->size;
635 : 1509 : slab->age = ma->age;
636 : : pl_assert(slab->used >= 0);
637 : :
638 : 1509 : pl_mutex_unlock(&slab->lock);
639 : :
640 : 1642 : done:
641 : 1642 : *slice = (struct vk_memslice) {0};
642 : 1642 : }
643 : :
644 : : static inline bool pool_params_eq(const struct vk_malloc_params *a,
645 : : const struct vk_malloc_params *b)
646 : : {
647 : 19668 : return a->reqs.size == b->reqs.size &&
648 [ + - ]: 9834 : a->reqs.alignment == b->reqs.alignment &&
649 [ + + ]: 9834 : a->reqs.memoryTypeBits == b->reqs.memoryTypeBits &&
650 [ + + ]: 6750 : a->required == b->required &&
651 : 2014 : a->optimal == b->optimal &&
652 [ + + ]: 2014 : a->buf_usage == b->buf_usage &&
653 [ + + ]: 1499 : a->export_handle == b->export_handle;
654 : : }
655 : :
656 : 1509 : static struct vk_pool *find_pool(struct vk_malloc *ma,
657 : : const struct vk_malloc_params *params)
658 : : {
659 [ - + ]: 1509 : pl_assert(!params->import_handle);
660 [ - + ]: 1509 : pl_assert(!params->ded_image);
661 : :
662 : 1509 : struct vk_malloc_params fixed = *params;
663 : : fixed.reqs.alignment = 0;
664 : : fixed.reqs.size = 0;
665 : 1509 : fixed.shared_mem = (struct pl_shared_mem) {0};
666 : :
667 [ + + ]: 9855 : for (int i = 0; i < ma->pools.num; i++) {
668 [ + - ]: 9834 : if (pool_params_eq(&ma->pools.elem[i].params, &fixed))
669 : 1488 : return &ma->pools.elem[i];
670 : : }
671 : :
672 : : // Not found => add it
673 [ + + + + : 21 : PL_ARRAY_GROW(ma, ma->pools);
- + ]
674 : 21 : size_t idx = ma->pools.num++;
675 : 21 : ma->pools.elem[idx] = (struct vk_pool) {
676 : : .params = fixed,
677 : : .index = idx,
678 : : };
679 : 21 : return &ma->pools.elem[idx];
680 : : }
681 : :
682 : : // Returns a suitable memory page from the pool. A new slab will be allocated
683 : : // under the hood, if necessary.
684 : : //
685 : : // Note: This locks the slab it returns
686 : 1509 : static struct vk_slab *pool_get_page(struct vk_malloc *ma, struct vk_pool *pool,
687 : : size_t size, size_t align,
688 : : VkDeviceSize *offset)
689 : : {
690 : : struct vk_slab *slab = NULL;
691 : : int slab_pages = MINIMUM_PAGE_COUNT;
692 : 1509 : size = PL_ALIGN2(size, PAGE_SIZE_ALIGN);
693 [ + - ]: 1509 : const size_t pagesize = PL_ALIGN(size, align);
694 : :
695 [ + + ]: 2490 : for (int i = 0; i < pool->slabs.num; i++) {
696 : 2442 : slab = pool->slabs.elem[i];
697 [ + + ]: 2442 : if (slab->pagesize < size)
698 : 828 : continue;
699 [ + + ]: 1614 : if (slab->pagesize > pagesize * MINIMUM_PAGE_COUNT) // rough heuristic
700 : 101 : continue;
701 [ - + ]: 1513 : if (slab->pagesize % align)
702 : 0 : continue;
703 : :
704 : 1513 : pl_mutex_lock(&slab->lock);
705 : 1513 : int page_idx = __builtin_ffsll(slab->spacemap);
706 [ + + ]: 1513 : if (!page_idx--) {
707 : 52 : pl_mutex_unlock(&slab->lock);
708 : : // Increase the number of slabs to allocate for new slabs the
709 : : // more existing full slabs exist for this size range
710 [ + - ]: 52 : slab_pages = PL_MIN(slab_pages << 1, MAXIMUM_PAGE_COUNT);
711 : 52 : continue;
712 : : }
713 : :
714 : 1461 : slab->spacemap ^= 0x1LLU << page_idx;
715 : 1461 : *offset = page_idx * slab->pagesize;
716 : 1461 : return slab;
717 : : }
718 : :
719 : : // Otherwise, allocate a new vk_slab and append it to the list.
720 : 48 : VkDeviceSize slab_size = slab_pages * pagesize;
721 : : pl_static_assert(MINIMUM_SLAB_SIZE <= PAGE_SIZE_ALIGN * MAXIMUM_PAGE_COUNT);
722 : 48 : const VkDeviceSize max_slab_size = ma->maximum_page_size * MINIMUM_PAGE_COUNT;
723 [ - + ]: 48 : pl_assert(pagesize <= ma->maximum_page_size);
724 [ + + ]: 48 : slab_size = PL_CLAMP(slab_size, MINIMUM_SLAB_SIZE, max_slab_size);
725 : 48 : slab_pages = slab_size / pagesize;
726 : 48 : slab_size = slab_pages * pagesize; // max_slab_size may be npot2, trim excess
727 : :
728 : 48 : struct vk_malloc_params params = pool->params;
729 : 48 : params.reqs.size = slab_size;
730 : :
731 : : // Don't hold the lock while allocating the slab, because it can be a
732 : : // potentially very costly operation.
733 : 48 : pl_mutex_unlock(&ma->lock);
734 : 48 : slab = slab_alloc(ma, ¶ms);
735 : 48 : pl_mutex_lock(&ma->lock);
736 [ + - ]: 48 : if (!slab)
737 : : return NULL;
738 : 48 : pl_mutex_lock(&slab->lock);
739 : :
740 [ + + ]: 48 : slab->spacemap = (slab_pages == sizeof(uint64_t) * 8) ? ~0LLU : ~(~0LLU << slab_pages);
741 : 48 : slab->pagesize = pagesize;
742 [ + + - + : 48 : PL_ARRAY_APPEND(NULL, pool->slabs, slab);
- + ]
743 : :
744 : : // Return the first page in this newly allocated slab
745 : 48 : slab->spacemap ^= 0x1;
746 : 48 : *offset = 0;
747 : 48 : return slab;
748 : : }
749 : :
750 : 24 : static bool vk_malloc_import(struct vk_malloc *ma, struct vk_memslice *out,
751 : : const struct vk_malloc_params *params)
752 : : {
753 : 24 : struct vk_ctx *vk = ma->vk;
754 : : VkExternalMemoryHandleTypeFlagBitsKHR vk_handle_type;
755 : 24 : vk_handle_type = vk_mem_handle_type(params->import_handle);
756 : :
757 : : struct vk_slab *slab = NULL;
758 : : const struct pl_shared_mem *shmem = ¶ms->shared_mem;
759 : :
760 : 24 : VkMemoryDedicatedAllocateInfoKHR dinfo = {
761 : : .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
762 : 24 : .image = params->ded_image,
763 : : };
764 : :
765 : 24 : VkImportMemoryFdInfoKHR fdinfo = {
766 : : .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
767 : : .handleType = vk_handle_type,
768 : : .fd = -1,
769 : : };
770 : :
771 : 24 : VkImportMemoryHostPointerInfoEXT ptrinfo = {
772 : : .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT,
773 : : .handleType = vk_handle_type,
774 : : };
775 : :
776 : 24 : VkMemoryAllocateInfo ainfo = {
777 : : .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
778 : 24 : .allocationSize = shmem->size,
779 : : };
780 : :
781 [ - + ]: 24 : if (params->ded_image)
782 : 0 : vk_link_struct(&ainfo, &dinfo);
783 : :
784 : 24 : VkBuffer buffer = VK_NULL_HANDLE;
785 : 24 : VkMemoryRequirements reqs = params->reqs;
786 : :
787 [ + + ]: 24 : if (params->buf_usage) {
788 : 22 : uint32_t qfs[3] = {0};
789 [ - + ]: 22 : pl_assert(vk->pools.num <= PL_ARRAY_SIZE(qfs));
790 [ + + ]: 44 : for (int i = 0; i < vk->pools.num; i++)
791 : 22 : qfs[i] = vk->pools.elem[i]->qf;
792 : :
793 : 22 : VkExternalMemoryBufferCreateInfoKHR ext_buf_info = {
794 : : .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
795 : : .handleTypes = vk_handle_type,
796 : : };
797 : :
798 : 22 : VkBufferCreateInfo binfo = {
799 : : .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
800 : : .pNext = &ext_buf_info,
801 : 22 : .size = shmem->size,
802 : : .usage = params->buf_usage,
803 : : .sharingMode = vk->pools.num > 1 ? VK_SHARING_MODE_CONCURRENT
804 : 22 : : VK_SHARING_MODE_EXCLUSIVE,
805 : : .queueFamilyIndexCount = vk->pools.num,
806 : : .pQueueFamilyIndices = qfs,
807 : : };
808 : :
809 [ - + ]: 22 : VK(vk->CreateBuffer(vk->dev, &binfo, PL_VK_ALLOC, &buffer));
810 [ + - ]: 22 : PL_VK_NAME(BUFFER, buffer, "imported");
811 : :
812 : 22 : vk->GetBufferMemoryRequirements(vk->dev, buffer, &reqs);
813 : : }
814 : :
815 [ - + ]: 24 : if (reqs.size > shmem->size) {
816 : 0 : PL_ERR(vk, "Imported object requires %zu bytes, larger than the "
817 : : "provided size %zu!",
818 : : (size_t) reqs.size, shmem->size);
819 : 0 : goto error;
820 : : }
821 : :
822 [ + + - + ]: 24 : if (shmem->offset % reqs.alignment || shmem->offset % params->reqs.alignment) {
823 : 4 : PL_ERR(vk, "Imported object offset %zu conflicts with alignment %zu!",
824 : : shmem->offset, pl_lcm(reqs.alignment, params->reqs.alignment));
825 : 4 : goto error;
826 : : }
827 : :
828 [ + + - - ]: 20 : switch (params->import_handle) {
829 : : #ifdef PL_HAVE_UNIX
830 : 4 : case PL_HANDLE_DMA_BUF: {
831 [ - + ]: 4 : if (!vk->GetMemoryFdPropertiesKHR) {
832 : 0 : PL_ERR(vk, "Importing PL_HANDLE_DMA_BUF requires %s.",
833 : : VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME);
834 : 0 : goto error;
835 : : }
836 : :
837 : 4 : VkMemoryFdPropertiesKHR fdprops = {
838 : : .sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR,
839 : : };
840 : :
841 [ - + ]: 4 : VK(vk->GetMemoryFdPropertiesKHR(vk->dev,
842 : : vk_handle_type,
843 : : shmem->handle.fd,
844 : : &fdprops));
845 : :
846 : : // We dup() the fd to make it safe to import the same original fd
847 : : // multiple times.
848 : 4 : fdinfo.fd = dup(shmem->handle.fd);
849 [ - + ]: 4 : if (fdinfo.fd == -1) {
850 : 0 : PL_ERR(vk, "Failed to dup() fd (%d) when importing memory: %s",
851 : : fdinfo.fd, strerror(errno));
852 : 0 : goto error;
853 : : }
854 : :
855 : 4 : reqs.memoryTypeBits &= fdprops.memoryTypeBits;
856 : 4 : vk_link_struct(&ainfo, &fdinfo);
857 : 4 : break;
858 : : }
859 : : #else // !PL_HAVE_UNIX
860 : : case PL_HANDLE_DMA_BUF:
861 : : PL_ERR(vk, "PL_HANDLE_DMA_BUF requires building with UNIX support!");
862 : : goto error;
863 : : #endif
864 : :
865 : 16 : case PL_HANDLE_HOST_PTR: {
866 : 16 : VkMemoryHostPointerPropertiesEXT ptrprops = {
867 : : .sType = VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT,
868 : : };
869 : :
870 [ - + ]: 16 : VK(vk->GetMemoryHostPointerPropertiesEXT(vk->dev, vk_handle_type,
871 : : shmem->handle.ptr,
872 : : &ptrprops));
873 : :
874 : 16 : ptrinfo.pHostPointer = (void *) shmem->handle.ptr;
875 : 16 : reqs.memoryTypeBits &= ptrprops.memoryTypeBits;
876 : 16 : vk_link_struct(&ainfo, &ptrinfo);
877 : 16 : break;
878 : : }
879 : :
880 : 0 : case PL_HANDLE_FD:
881 : : case PL_HANDLE_WIN32:
882 : : case PL_HANDLE_WIN32_KMT:
883 : : case PL_HANDLE_IOSURFACE:
884 : : case PL_HANDLE_MTL_TEX:
885 : 0 : PL_ERR(vk, "vk_malloc_import: unsupported handle type %d",
886 : : params->import_handle);
887 : 0 : goto error;
888 : : }
889 : :
890 [ - + ]: 20 : if (!find_best_memtype(ma, reqs.memoryTypeBits, params, &ainfo.memoryTypeIndex)) {
891 : 0 : PL_ERR(vk, "No compatible memory types offered for imported memory!");
892 : 0 : goto error;
893 : : }
894 : :
895 : 20 : VkDeviceMemory vkmem = VK_NULL_HANDLE;
896 [ - + ]: 20 : VK(vk->AllocateMemory(vk->dev, &ainfo, PL_VK_ALLOC, &vkmem));
897 : :
898 : 20 : slab = pl_alloc_ptr(NULL, slab);
899 : 20 : *slab = (struct vk_slab) {
900 : : .mem = vkmem,
901 : : .dedicated = true,
902 : : .imported = true,
903 : : .buffer = buffer,
904 : 20 : .size = shmem->size,
905 : 20 : .handle_type = params->import_handle,
906 : : };
907 [ - + ]: 20 : pl_mutex_init(&slab->lock);
908 : :
909 : 20 : *out = (struct vk_memslice) {
910 : : .vkmem = vkmem,
911 : : .buf = buffer,
912 : 20 : .size = shmem->size - shmem->offset,
913 : 20 : .offset = shmem->offset,
914 : 20 : .shared_mem = *shmem,
915 : : .priv = slab,
916 : : };
917 : :
918 [ + + - - ]: 20 : switch (params->import_handle) {
919 : 4 : case PL_HANDLE_DMA_BUF:
920 : : case PL_HANDLE_FD:
921 [ + - ]: 8 : PL_TRACE(vk, "Imported %s bytes from fd: %d%s",
922 : : PRINT_SIZE(slab->size), shmem->handle.fd,
923 : : params->ded_image ? " (dedicated)" : "");
924 : : // fd ownership is transferred at this point.
925 : 4 : slab->handle.fd = fdinfo.fd;
926 : 4 : fdinfo.fd = -1;
927 : 4 : break;
928 : 16 : case PL_HANDLE_HOST_PTR:
929 [ + - ]: 32 : PL_TRACE(vk, "Imported %s bytes from ptr: %p%s",
930 : : PRINT_SIZE(slab->size), shmem->handle.ptr,
931 : : params->ded_image ? " (dedicated" : "");
932 : 16 : slab->handle.ptr = ptrinfo.pHostPointer;
933 : 16 : break;
934 : : case PL_HANDLE_WIN32:
935 : : case PL_HANDLE_WIN32_KMT:
936 : : case PL_HANDLE_IOSURFACE:
937 : : case PL_HANDLE_MTL_TEX:
938 : : break;
939 : : }
940 : :
941 : 20 : VkMemoryPropertyFlags flags = ma->props.memoryTypes[ainfo.memoryTypeIndex].propertyFlags;
942 [ + - ]: 20 : if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
943 [ - + ]: 20 : VK(vk->MapMemory(vk->dev, slab->mem, 0, VK_WHOLE_SIZE, 0, &slab->data));
944 : 20 : slab->coherent = flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
945 : 20 : out->data = (uint8_t *) slab->data + out->offset;
946 : 20 : out->coherent = slab->coherent;
947 [ + + ]: 20 : if (!slab->coherent) {
948 : : // Use entire buffer range, since this is a dedicated memory
949 : : // allocation. This avoids issues with noncoherent atomicity
950 : 2 : out->map_offset = 0;
951 : 2 : out->map_size = VK_WHOLE_SIZE;
952 : :
953 : : // Mapping does not implicitly invalidate mapped memory
954 [ - + ]: 2 : VK(vk->InvalidateMappedMemoryRanges(vk->dev, 1, &(VkMappedMemoryRange) {
955 : : .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
956 : : .memory = slab->mem,
957 : : .offset = out->map_offset,
958 : : .size = out->map_size,
959 : : }));
960 : : }
961 : : }
962 : :
963 [ + + ]: 20 : if (buffer)
964 [ + - ]: 18 : VK(vk->BindBufferMemory(vk->dev, buffer, vkmem, 0));
965 : :
966 : : return true;
967 : :
968 : 4 : error:
969 [ + - ]: 4 : if (params->debug_tag)
970 : 4 : PL_ERR(vk, " for malloc: %s", params->debug_tag);
971 : 4 : vk->DestroyBuffer(vk->dev, buffer, PL_VK_ALLOC);
972 : : #ifdef PL_HAVE_UNIX
973 [ - + ]: 4 : if (fdinfo.fd > -1)
974 : 0 : close(fdinfo.fd);
975 : : #endif
976 : 4 : pl_free(slab);
977 : 4 : *out = (struct vk_memslice) {0};
978 : 4 : return false;
979 : : }
980 : :
981 : 15 : size_t vk_malloc_avail(struct vk_malloc *ma, VkMemoryPropertyFlags flags)
982 : : {
983 : : size_t avail = 0;
984 [ + + ]: 75 : for (int i = 0; i < ma->props.memoryTypeCount; i++) {
985 : : const VkMemoryType *mtype = &ma->props.memoryTypes[i];
986 [ + + ]: 60 : if ((mtype->propertyFlags & flags) != flags)
987 : 6 : continue;
988 : 54 : avail = PL_MAX(avail, ma->props.memoryHeaps[mtype->heapIndex].size);
989 : : }
990 : :
991 : 15 : return avail;
992 : : }
993 : :
994 : 1533 : bool vk_malloc_slice(struct vk_malloc *ma, struct vk_memslice *out,
995 : : const struct vk_malloc_params *params)
996 : : {
997 : 1533 : struct vk_ctx *vk = ma->vk;
998 [ + + - + ]: 1533 : pl_assert(!params->import_handle || !params->export_handle);
999 [ + + ]: 1533 : if (params->import_handle)
1000 : 24 : return vk_malloc_import(ma, out, params);
1001 : :
1002 [ - + ]: 1509 : pl_assert(params->reqs.size);
1003 : : size_t size = params->reqs.size;
1004 : 1509 : size_t align = params->reqs.alignment;
1005 : 1509 : align = pl_lcm(align, vk->props.limits.bufferImageGranularity);
1006 : 1509 : align = pl_lcm(align, vk->props.limits.nonCoherentAtomSize);
1007 : :
1008 : : struct vk_slab *slab;
1009 : : VkDeviceSize offset;
1010 : :
1011 [ + - - + ]: 1509 : if (params->ded_image || size > ma->maximum_page_size) {
1012 : 0 : slab = slab_alloc(ma, params);
1013 [ # # ]: 0 : if (!slab)
1014 : : return false;
1015 : 0 : slab->dedicated = true;
1016 : 0 : offset = 0;
1017 : : } else {
1018 : 1509 : pl_mutex_lock(&ma->lock);
1019 : 1509 : struct vk_pool *pool = find_pool(ma, params);
1020 : 1509 : slab = pool_get_page(ma, pool, size, align, &offset);
1021 : 1509 : pl_mutex_unlock(&ma->lock);
1022 [ - + ]: 1509 : if (!slab) {
1023 : 0 : PL_ERR(ma->vk, "No slab to serve request for %s bytes (with "
1024 : : "alignment 0x%zx) in pool %d!",
1025 : : PRINT_SIZE(size), align, pool->index);
1026 : : return false;
1027 : : }
1028 : :
1029 : : // For accounting, just treat the alignment as part of the used size.
1030 : : // Doing it this way makes sure that the sizes reported to vk_memslice
1031 : : // consumers are always aligned properly.
1032 [ + - ]: 1509 : size = PL_ALIGN(size, align);
1033 : 1509 : slab->used += size;
1034 : 1509 : slab->age = ma->age;
1035 [ + + ]: 1509 : if (params->debug_tag)
1036 : 1497 : slab->debug_tag = params->debug_tag;
1037 : 1509 : pl_mutex_unlock(&slab->lock);
1038 : : }
1039 : :
1040 [ - + ]: 1509 : pl_assert(offset % align == 0);
1041 : 1509 : *out = (struct vk_memslice) {
1042 : 1509 : .vkmem = slab->mem,
1043 : : .offset = offset,
1044 : : .size = size,
1045 : 1509 : .buf = slab->buffer,
1046 : 1509 : .data = slab->data ? (uint8_t *) slab->data + offset : 0x0,
1047 : 1509 : .coherent = slab->coherent,
1048 [ + - ]: 1509 : .map_offset = slab->data ? offset : 0,
1049 [ - + ]: 1509 : .map_size = slab->data ? size : 0,
1050 : : .priv = slab,
1051 : : .shared_mem = {
1052 : : .handle = slab->handle,
1053 : : .offset = offset,
1054 [ + - ]: 1509 : .size = slab->size,
1055 : : },
1056 : : };
1057 : 1509 : return true;
1058 : : }
|