diff options
author | Mike Blumenkrantz <michael.blumenkrantz@gmail.com> | 2021-05-13 07:33:44 -0400 |
---|---|---|
committer | Marge Bot <eric+marge@anholt.net> | 2021-08-17 13:21:28 +0000 |
commit | 40fdb3212c3ded2150faf952dc2b669991bfbf94 (patch) | |
tree | b18d57928f8ca2a02453d0791dff14a0cc63ea83 | |
parent | 5df677e996bb8cdbefbe4a789872734cbd174023 (diff) |
zink: add a suballocator
this is an aux/pipebuffer implementation borrowing heavily from the
one in radeonsi. it currently has the following limitations, which
will be resolved in a followup series:
* 32bit address space still explodes
* swapchain images still have separate memory handling
performance in games like Tomb Raider has been observed to increase by
over 1000%
SQUASHED: simplify get_memory_type_index()
now that the heaps are enumerated, this can be reduced to a simple
array index with a fallback
Reviewed-by: Dave Airlie <airlied@redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/12146>
-rw-r--r-- | src/gallium/drivers/zink/meson.build | 1 | ||||
-rw-r--r-- | src/gallium/drivers/zink/zink_bo.c | 1014 | ||||
-rw-r--r-- | src/gallium/drivers/zink/zink_bo.h | 270 | ||||
-rw-r--r-- | src/gallium/drivers/zink/zink_context.c | 70 | ||||
-rw-r--r-- | src/gallium/drivers/zink/zink_resource.c | 168 | ||||
-rw-r--r-- | src/gallium/drivers/zink/zink_resource.h | 83 | ||||
-rw-r--r-- | src/gallium/drivers/zink/zink_screen.c | 44 | ||||
-rw-r--r-- | src/gallium/drivers/zink/zink_screen.h | 25 |
8 files changed, 1425 insertions, 250 deletions
diff --git a/src/gallium/drivers/zink/meson.build b/src/gallium/drivers/zink/meson.build index 28aae9cdfc4..87501df180e 100644 --- a/src/gallium/drivers/zink/meson.build +++ b/src/gallium/drivers/zink/meson.build @@ -24,6 +24,7 @@ files_libzink = files( 'nir_to_spirv/spirv_builder.c', 'zink_batch.c', 'zink_blit.c', + 'zink_bo.c', 'zink_clear.c', 'zink_compiler.c', 'zink_context.c', diff --git a/src/gallium/drivers/zink/zink_bo.c b/src/gallium/drivers/zink/zink_bo.c new file mode 100644 index 00000000000..b73d6cfbb9d --- /dev/null +++ b/src/gallium/drivers/zink/zink_bo.c @@ -0,0 +1,1014 @@ +/* + * Copyright © 2011 Marek Olšák <maraeo@gmail.com> + * Copyright © 2015 Advanced Micro Devices, Inc. + * Copyright © 2021 Valve Corporation + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS + * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * Authors: + * Mike Blumenkrantz <michael.blumenkrantz@gmail.com> + */ + +#include "zink_bo.h" +#include "zink_resource.h" +#include "zink_screen.h" +#include "util/u_hash_table.h" + +struct zink_bo; + +struct zink_sparse_backing_chunk { + uint32_t begin, end; +}; + + +/* + * Sub-allocation information for a real buffer used as backing memory of a + * sparse buffer. + */ +struct zink_sparse_backing { + struct list_head list; + + struct zink_bo *bo; + + /* Sorted list of free chunks. */ + struct zink_sparse_backing_chunk *chunks; + uint32_t max_chunks; + uint32_t num_chunks; +}; + +struct zink_sparse_commitment { + struct zink_sparse_backing *backing; + uint32_t page; +}; + +struct zink_slab { + struct pb_slab base; + unsigned entry_size; + struct zink_bo *buffer; + struct zink_bo *entries; +}; + + +ALWAYS_INLINE static struct zink_slab * +zink_slab(struct pb_slab *pslab) +{ + return (struct zink_slab*)pslab; +} + +static struct pb_slabs * +get_slabs(struct zink_screen *screen, uint64_t size, enum zink_alloc_flag flags) +{ + //struct pb_slabs *bo_slabs = ((flags & RADEON_FLAG_ENCRYPTED) && screen->info.has_tmz_support) ? + //screen->bo_slabs_encrypted : screen->bo_slabs; + + struct pb_slabs *bo_slabs = screen->pb.bo_slabs; + /* Find the correct slab allocator for the given size. */ + for (unsigned i = 0; i < NUM_SLAB_ALLOCATORS; i++) { + struct pb_slabs *slabs = &bo_slabs[i]; + + if (size <= 1ULL << (slabs->min_order + slabs->num_orders - 1)) + return slabs; + } + + assert(0); + return NULL; +} + +/* Return the power of two size of a slab entry matching the input size. */ +static unsigned +get_slab_pot_entry_size(struct zink_screen *screen, unsigned size) +{ + unsigned entry_size = util_next_power_of_two(size); + unsigned min_entry_size = 1 << screen->pb.bo_slabs[0].min_order; + + return MAX2(entry_size, min_entry_size); +} + +/* Return the slab entry alignment. */ +static unsigned get_slab_entry_alignment(struct zink_screen *screen, unsigned size) +{ + unsigned entry_size = get_slab_pot_entry_size(screen, size); + + if (size <= entry_size * 3 / 4) + return entry_size / 4; + + return entry_size; +} + +static void +bo_destroy(struct zink_screen *screen, struct pb_buffer *pbuf) +{ + struct zink_bo *bo = zink_bo(pbuf); + + simple_mtx_lock(&screen->pb.bo_export_table_lock); + _mesa_hash_table_remove_key(screen->pb.bo_export_table, bo); + simple_mtx_unlock(&screen->pb.bo_export_table_lock); + + if (!bo->u.real.is_user_ptr && bo->u.real.cpu_ptr) { + bo->u.real.map_count = 1; + bo->u.real.cpu_ptr = NULL; + zink_bo_unmap(screen, bo); + } + + vkFreeMemory(screen->dev, bo->mem, NULL); + + simple_mtx_destroy(&bo->lock); + FREE(bo); +} + +static bool +bo_can_reclaim(struct zink_screen *screen, struct pb_buffer *pbuf) +{ + struct zink_bo *bo = zink_bo(pbuf); + + return zink_screen_usage_check_completion(screen, bo->reads) && zink_screen_usage_check_completion(screen, bo->writes); +} + +static bool +bo_can_reclaim_slab(void *priv, struct pb_slab_entry *entry) +{ + struct zink_bo *bo = container_of(entry, struct zink_bo, u.slab.entry); + + return bo_can_reclaim(priv, &bo->base); +} + +static void +bo_slab_free(struct zink_screen *screen, struct pb_slab *pslab) +{ + struct zink_slab *slab = zink_slab(pslab); + ASSERTED unsigned slab_size = slab->buffer->base.size; + + assert(slab->base.num_entries * slab->entry_size <= slab_size); + FREE(slab->entries); + zink_bo_unref(screen, slab->buffer); + FREE(slab); +} + +static void +bo_slab_destroy(struct zink_screen *screen, struct pb_buffer *pbuf) +{ + struct zink_bo *bo = zink_bo(pbuf); + + assert(!bo->mem); + + //if (bo->base.usage & RADEON_FLAG_ENCRYPTED) + //pb_slab_free(get_slabs(screen, bo->base.size, RADEON_FLAG_ENCRYPTED), &bo->u.slab.entry); + //else + pb_slab_free(get_slabs(screen, bo->base.size, 0), &bo->u.slab.entry); +} + +static void +clean_up_buffer_managers(struct zink_screen *screen) +{ + for (unsigned i = 0; i < NUM_SLAB_ALLOCATORS; i++) { + pb_slabs_reclaim(&screen->pb.bo_slabs[i]); + //if (screen->info.has_tmz_support) + //pb_slabs_reclaim(&screen->bo_slabs_encrypted[i]); + } + + pb_cache_release_all_buffers(&screen->pb.bo_cache); +} + +static unsigned +get_optimal_alignment(struct zink_screen *screen, uint64_t size, unsigned alignment) +{ + /* Increase the alignment for faster address translation and better memory + * access pattern. + */ + if (size >= 4096) { + alignment = MAX2(alignment, 4096); + } else if (size) { + unsigned msb = util_last_bit(size); + + alignment = MAX2(alignment, 1u << (msb - 1)); + } + return alignment; +} + +static void +bo_destroy_or_cache(struct zink_screen *screen, struct pb_buffer *pbuf) +{ + struct zink_bo *bo = zink_bo(pbuf); + + assert(bo->mem); /* slab buffers have a separate vtbl */ + bo->reads = NULL; + bo->writes = NULL; + + if (bo->u.real.use_reusable_pool) + pb_cache_add_buffer(bo->cache_entry); + else + bo_destroy(screen, pbuf); +} + +static const struct pb_vtbl bo_vtbl = { + /* Cast to void* because one of the function parameters is a struct pointer instead of void*. */ + (void*)bo_destroy_or_cache + /* other functions are never called */ +}; + +static struct zink_bo * +bo_create_internal(struct zink_screen *screen, + uint64_t size, + unsigned alignment, + enum zink_heap heap, + unsigned flags) +{ + struct zink_bo *bo; + bool init_pb_cache; + + /* too big for vk alloc */ + if (size > UINT32_MAX) + return NULL; + + alignment = get_optimal_alignment(screen, size, alignment); + + /* all non-suballocated bo can cache */ + init_pb_cache = true; + + bo = CALLOC(1, sizeof(struct zink_bo) + init_pb_cache * sizeof(struct pb_cache_entry)); + if (!bo) { + return NULL; + } + + if (init_pb_cache) { + bo->u.real.use_reusable_pool = true; + pb_cache_init_entry(&screen->pb.bo_cache, bo->cache_entry, &bo->base, heap); + } + + VkMemoryAllocateInfo mai = {0}; + mai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mai.allocationSize = size; + mai.memoryTypeIndex = screen->heap_map[heap]; + if (screen->info.mem_props.memoryTypes[mai.memoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + alignment = MAX2(alignment, screen->info.props.limits.minMemoryMapAlignment); + mai.allocationSize = align(mai.allocationSize, screen->info.props.limits.minMemoryMapAlignment); + } + VkResult ret = vkAllocateMemory(screen->dev, &mai, NULL, &bo->mem); + if (!zink_screen_handle_vkresult(screen, ret)) + goto fail; + + simple_mtx_init(&bo->lock, mtx_plain); + pipe_reference_init(&bo->base.reference, 1); + bo->base.alignment_log2 = util_logbase2(alignment); + bo->base.size = size; + bo->base.vtbl = &bo_vtbl; + bo->base.placement = vk_domain_from_heap(heap); + bo->base.usage = flags; + bo->unique_id = p_atomic_inc_return(&screen->pb.next_bo_unique_id); + + return bo; + +fail: + bo_destroy(screen, (void*)bo); + return NULL; +} + +/* + * Attempt to allocate the given number of backing pages. Fewer pages may be + * allocated (depending on the fragmentation of existing backing buffers), + * which will be reflected by a change to *pnum_pages. + */ +static struct zink_sparse_backing * +sparse_backing_alloc(struct zink_screen *screen, struct zink_bo *bo, + uint32_t *pstart_page, uint32_t *pnum_pages) +{ + struct zink_sparse_backing *best_backing; + unsigned best_idx; + uint32_t best_num_pages; + + best_backing = NULL; + best_idx = 0; + best_num_pages = 0; + + /* This is a very simple and inefficient best-fit algorithm. */ + list_for_each_entry(struct zink_sparse_backing, backing, &bo->u.sparse.backing, list) { + for (unsigned idx = 0; idx < backing->num_chunks; ++idx) { + uint32_t cur_num_pages = backing->chunks[idx].end - backing->chunks[idx].begin; + if ((best_num_pages < *pnum_pages && cur_num_pages > best_num_pages) || + (best_num_pages > *pnum_pages && cur_num_pages < best_num_pages)) { + best_backing = backing; + best_idx = idx; + best_num_pages = cur_num_pages; + } + } + } + + /* Allocate a new backing buffer if necessary. */ + if (!best_backing) { + struct pb_buffer *buf; + uint64_t size; + uint32_t pages; + + best_backing = CALLOC_STRUCT(zink_sparse_backing); + if (!best_backing) + return NULL; + + best_backing->max_chunks = 4; + best_backing->chunks = CALLOC(best_backing->max_chunks, + sizeof(*best_backing->chunks)); + if (!best_backing->chunks) { + FREE(best_backing); + return NULL; + } + + assert(bo->u.sparse.num_backing_pages < DIV_ROUND_UP(bo->base.size, ZINK_SPARSE_BUFFER_PAGE_SIZE)); + + size = MIN3(bo->base.size / 16, + 8 * 1024 * 1024, + bo->base.size - (uint64_t)bo->u.sparse.num_backing_pages * ZINK_SPARSE_BUFFER_PAGE_SIZE); + size = MAX2(size, ZINK_SPARSE_BUFFER_PAGE_SIZE); + + buf = zink_bo_create(screen, size, ZINK_SPARSE_BUFFER_PAGE_SIZE, + bo->base.placement, ZINK_ALLOC_NO_SUBALLOC); + if (!buf) { + FREE(best_backing->chunks); + FREE(best_backing); + return NULL; + } + + /* We might have gotten a bigger buffer than requested via caching. */ + pages = buf->size / ZINK_SPARSE_BUFFER_PAGE_SIZE; + + best_backing->bo = zink_bo(buf); + best_backing->num_chunks = 1; + best_backing->chunks[0].begin = 0; + best_backing->chunks[0].end = pages; + + list_add(&best_backing->list, &bo->u.sparse.backing); + bo->u.sparse.num_backing_pages += pages; + + best_idx = 0; + best_num_pages = pages; + } + + *pnum_pages = MIN2(*pnum_pages, best_num_pages); + *pstart_page = best_backing->chunks[best_idx].begin; + best_backing->chunks[best_idx].begin += *pnum_pages; + + if (best_backing->chunks[best_idx].begin >= best_backing->chunks[best_idx].end) { + memmove(&best_backing->chunks[best_idx], &best_backing->chunks[best_idx + 1], + sizeof(*best_backing->chunks) * (best_backing->num_chunks - best_idx - 1)); + best_backing->num_chunks--; + } + + return best_backing; +} + +static void +sparse_free_backing_buffer(struct zink_screen *screen, struct zink_bo *bo, + struct zink_sparse_backing *backing) +{ + bo->u.sparse.num_backing_pages -= backing->bo->base.size / ZINK_SPARSE_BUFFER_PAGE_SIZE; + + list_del(&backing->list); + zink_bo_unref(screen, backing->bo); + FREE(backing->chunks); + FREE(backing); +} + +/* + * Return a range of pages from the given backing buffer back into the + * free structure. + */ +static bool +sparse_backing_free(struct zink_screen *screen, struct zink_bo *bo, + struct zink_sparse_backing *backing, + uint32_t start_page, uint32_t num_pages) +{ + uint32_t end_page = start_page + num_pages; + unsigned low = 0; + unsigned high = backing->num_chunks; + + /* Find the first chunk with begin >= start_page. */ + while (low < high) { + unsigned mid = low + (high - low) / 2; + + if (backing->chunks[mid].begin >= start_page) + high = mid; + else + low = mid + 1; + } + + assert(low >= backing->num_chunks || end_page <= backing->chunks[low].begin); + assert(low == 0 || backing->chunks[low - 1].end <= start_page); + + if (low > 0 && backing->chunks[low - 1].end == start_page) { + backing->chunks[low - 1].end = end_page; + + if (low < backing->num_chunks && end_page == backing->chunks[low].begin) { + backing->chunks[low - 1].end = backing->chunks[low].end; + memmove(&backing->chunks[low], &backing->chunks[low + 1], + sizeof(*backing->chunks) * (backing->num_chunks - low - 1)); + backing->num_chunks--; + } + } else if (low < backing->num_chunks && end_page == backing->chunks[low].begin) { + backing->chunks[low].begin = start_page; + } else { + if (backing->num_chunks >= backing->max_chunks) { + unsigned new_max_chunks = 2 * backing->max_chunks; + struct zink_sparse_backing_chunk *new_chunks = + REALLOC(backing->chunks, + sizeof(*backing->chunks) * backing->max_chunks, + sizeof(*backing->chunks) * new_max_chunks); + if (!new_chunks) + return false; + + backing->max_chunks = new_max_chunks; + backing->chunks = new_chunks; + } + + memmove(&backing->chunks[low + 1], &backing->chunks[low], + sizeof(*backing->chunks) * (backing->num_chunks - low)); + backing->chunks[low].begin = start_page; + backing->chunks[low].end = end_page; + backing->num_chunks++; + } + + if (backing->num_chunks == 1 && backing->chunks[0].begin == 0 && + backing->chunks[0].end == backing->bo->base.size / ZINK_SPARSE_BUFFER_PAGE_SIZE) + sparse_free_backing_buffer(screen, bo, backing); + + return true; +} + +static void +bo_sparse_destroy(struct zink_screen *screen, struct pb_buffer *pbuf) +{ + struct zink_bo *bo = zink_bo(pbuf); + + assert(!bo->mem && bo->base.usage & ZINK_ALLOC_SPARSE); + + while (!list_is_empty(&bo->u.sparse.backing)) { + sparse_free_backing_buffer(screen, bo, + container_of(bo->u.sparse.backing.next, + struct zink_sparse_backing, list)); + } + + FREE(bo->u.sparse.commitments); + simple_mtx_destroy(&bo->lock); + FREE(bo); +} + +static const struct pb_vtbl bo_sparse_vtbl = { + /* Cast to void* because one of the function parameters is a struct pointer instead of void*. */ + (void*)bo_sparse_destroy + /* other functions are never called */ +}; + +static struct pb_buffer * +bo_sparse_create(struct zink_screen *screen, uint64_t size) +{ + struct zink_bo *bo; + + /* We use 32-bit page numbers; refuse to attempt allocating sparse buffers + * that exceed this limit. This is not really a restriction: we don't have + * that much virtual address space anyway. + */ + if (size > (uint64_t)INT32_MAX * ZINK_SPARSE_BUFFER_PAGE_SIZE) + return NULL; + + bo = CALLOC_STRUCT(zink_bo); + if (!bo) + return NULL; + + simple_mtx_init(&bo->lock, mtx_plain); + pipe_reference_init(&bo->base.reference, 1); + bo->base.alignment_log2 = util_logbase2(ZINK_SPARSE_BUFFER_PAGE_SIZE); + bo->base.size = size; + bo->base.vtbl = &bo_sparse_vtbl; + bo->base.placement = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + bo->unique_id = p_atomic_inc_return(&screen->pb.next_bo_unique_id); + bo->base.usage = ZINK_ALLOC_SPARSE; + + bo->u.sparse.num_va_pages = DIV_ROUND_UP(size, ZINK_SPARSE_BUFFER_PAGE_SIZE); + bo->u.sparse.commitments = CALLOC(bo->u.sparse.num_va_pages, + sizeof(*bo->u.sparse.commitments)); + if (!bo->u.sparse.commitments) + goto error_alloc_commitments; + + list_inithead(&bo->u.sparse.backing); + + return &bo->base; + +error_alloc_commitments: + simple_mtx_destroy(&bo->lock); + FREE(bo); + return NULL; +} + +struct pb_buffer * +zink_bo_create(struct zink_screen *screen, uint64_t size, unsigned alignment, enum zink_heap heap, enum zink_alloc_flag flags) +{ + struct zink_bo *bo; + /* pull in sparse flag */ + flags |= zink_alloc_flags_from_heap(heap); + + //struct pb_slabs *slabs = ((flags & RADEON_FLAG_ENCRYPTED) && screen->info.has_tmz_support) ? + //screen->bo_slabs_encrypted : screen->bo_slabs; + struct pb_slabs *slabs = screen->pb.bo_slabs; + + struct pb_slabs *last_slab = &slabs[NUM_SLAB_ALLOCATORS - 1]; + unsigned max_slab_entry_size = 1 << (last_slab->min_order + last_slab->num_orders - 1); + + /* Sub-allocate small buffers from slabs. */ + if (!(flags & (ZINK_ALLOC_NO_SUBALLOC | ZINK_ALLOC_SPARSE)) && + size <= max_slab_entry_size) { + struct pb_slab_entry *entry; + + if (heap < 0 || heap >= ZINK_HEAP_MAX) + goto no_slab; + + unsigned alloc_size = size; + + /* Always use slabs for sizes less than 4 KB because the kernel aligns + * everything to 4 KB. + */ + if (size < alignment && alignment <= 4 * 1024) + alloc_size = alignment; + + if (alignment > get_slab_entry_alignment(screen, alloc_size)) { + /* 3/4 allocations can return too small alignment. Try again with a power of two + * allocation size. + */ + unsigned pot_size = get_slab_pot_entry_size(screen, alloc_size); + + if (alignment <= pot_size) { + /* This size works but wastes some memory to fulfil the alignment. */ + alloc_size = pot_size; + } else { + goto no_slab; /* can't fulfil alignment requirements */ + } + } + + struct pb_slabs *slabs = get_slabs(screen, alloc_size, flags); + entry = pb_slab_alloc(slabs, alloc_size, heap); + if (!entry) { + /* Clean up buffer managers and try again. */ + clean_up_buffer_managers(screen); + + entry = pb_slab_alloc(slabs, alloc_size, heap); + } + if (!entry) + return NULL; + + bo = container_of(entry, struct zink_bo, u.slab.entry); + pipe_reference_init(&bo->base.reference, 1); + bo->base.size = size; + assert(alignment <= 1 << bo->base.alignment_log2); + + return &bo->base; + } +no_slab: + + if (flags & ZINK_ALLOC_SPARSE) { + assert(ZINK_SPARSE_BUFFER_PAGE_SIZE % alignment == 0); + + return bo_sparse_create(screen, size); + } + + /* Align size to page size. This is the minimum alignment for normal + * BOs. Aligning this here helps the cached bufmgr. Especially small BOs, + * like constant/uniform buffers, can benefit from better and more reuse. + */ + if (heap == ZINK_HEAP_DEVICE_LOCAL_VISIBLE) { + size = align64(size, screen->info.props.limits.minMemoryMapAlignment); + alignment = align(alignment, screen->info.props.limits.minMemoryMapAlignment); + } + + bool use_reusable_pool = !(flags & ZINK_ALLOC_NO_SUBALLOC); + + if (use_reusable_pool) { + /* Get a buffer from the cache. */ + bo = (struct zink_bo*) + pb_cache_reclaim_buffer(&screen->pb.bo_cache, size, alignment, 0, heap); + if (bo) + return &bo->base; + } + + /* Create a new one. */ + bo = bo_create_internal(screen, size, alignment, heap, flags); + if (!bo) { + /* Clean up buffer managers and try again. */ + clean_up_buffer_managers(screen); + + bo = bo_create_internal(screen, size, alignment, heap, flags); + if (!bo) + return NULL; + } + + return &bo->base; +} + +void * +zink_bo_map(struct zink_screen *screen, struct zink_bo *bo) +{ + void *cpu = NULL; + uint64_t offset = 0; + struct zink_bo *real; + + if (bo->mem) { + real = bo; + } else { + real = bo->u.slab.real; + offset = bo->offset - real->offset; + } + + cpu = p_atomic_read(&real->u.real.cpu_ptr); + if (!cpu) { + simple_mtx_lock(&real->lock); + /* Must re-check due to the possibility of a race. Re-check need not + * be atomic thanks to the lock. */ + cpu = real->u.real.cpu_ptr; + if (!cpu) { + VkResult result = vkMapMemory(screen->dev, real->mem, 0, real->base.size, 0, &cpu); + if (result != VK_SUCCESS) { + simple_mtx_unlock(&real->lock); + return NULL; + } + p_atomic_set(&real->u.real.cpu_ptr, cpu); + } + simple_mtx_unlock(&real->lock); + } + p_atomic_inc(&real->u.real.map_count); + + return (uint8_t*)cpu + offset; +} + +void +zink_bo_unmap(struct zink_screen *screen, struct zink_bo *bo) +{ + struct zink_bo *real = bo->mem ? bo : bo->u.slab.real; + + assert(real->u.real.map_count != 0 && "too many unmaps"); + + if (p_atomic_dec_zero(&real->u.real.map_count)) { + p_atomic_set(&real->u.real.cpu_ptr, NULL); + vkUnmapMemory(screen->dev, real->mem); + } +} + + +static inline struct zink_screen ** +get_screen_ptr_for_commit(uint8_t *mem) +{ + return (struct zink_screen**)(mem + sizeof(VkBindSparseInfo) + sizeof(VkSparseBufferMemoryBindInfo) + sizeof(VkSparseMemoryBind)); +} + +static bool +resource_commit(struct zink_screen *screen, VkBindSparseInfo *sparse) +{ + VkQueue queue = screen->threaded ? screen->thread_queue : screen->queue; + + VkResult ret = vkQueueBindSparse(queue, 1, sparse, VK_NULL_HANDLE); + return zink_screen_handle_vkresult(screen, ret); +} + +static void +submit_resource_commit(void *data, void *gdata, int thread_index) +{ + struct zink_screen **screen = get_screen_ptr_for_commit(data); + resource_commit(*screen, data); + free(data); +} + +static bool +do_commit_single(struct zink_screen *screen, struct zink_resource *res, struct zink_bo *bo, uint32_t offset, uint32_t size, bool commit) +{ + + uint8_t *mem = malloc(sizeof(VkBindSparseInfo) + sizeof(VkSparseBufferMemoryBindInfo) + sizeof(VkSparseMemoryBind) + sizeof(void*)); + if (!mem) + return false; + VkBindSparseInfo *sparse = (void*)mem; + sparse->sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO; + sparse->pNext = NULL; + sparse->waitSemaphoreCount = 0; + sparse->bufferBindCount = 1; + sparse->imageOpaqueBindCount = 0; + sparse->imageBindCount = 0; + sparse->signalSemaphoreCount = 0; + + VkSparseBufferMemoryBindInfo *sparse_bind = (void*)(mem + sizeof(VkBindSparseInfo)); + sparse_bind->buffer = res->obj->buffer; + sparse_bind->bindCount = 1; + sparse->pBufferBinds = sparse_bind; + + VkSparseMemoryBind *mem_bind = (void*)(mem + sizeof(VkBindSparseInfo) + sizeof(VkSparseBufferMemoryBindInfo)); + mem_bind->resourceOffset = offset; + mem_bind->size = MIN2(res->base.b.width0 - offset, size); + mem_bind->memory = commit ? bo->mem : VK_NULL_HANDLE; + mem_bind->memoryOffset = 0; + mem_bind->flags = 0; + sparse_bind->pBinds = mem_bind; + + struct zink_screen **ptr = get_screen_ptr_for_commit(mem); + *ptr = screen; + + if (screen->threaded) { + /* this doesn't need any kind of fencing because any access to this resource + * will be automagically synchronized by queue dispatch */ + util_queue_add_job(&screen->flush_queue, mem, NULL, submit_resource_commit, NULL, 0); + } else { + bool ret = resource_commit(screen, sparse); + free(sparse); + return ret; + } + return true; +} + +bool +zink_bo_commit(struct zink_screen *screen, struct zink_resource *res, uint32_t offset, uint32_t size, bool commit) +{ + bool ok = true; + struct zink_bo *bo = res->obj->bo; + assert(offset % ZINK_SPARSE_BUFFER_PAGE_SIZE == 0); + assert(offset <= bo->base.size); + assert(size <= bo->base.size - offset); + assert(size % ZINK_SPARSE_BUFFER_PAGE_SIZE == 0 || offset + size == bo->base.size); + + struct zink_sparse_commitment *comm = bo->u.sparse.commitments; + + uint32_t va_page = offset / ZINK_SPARSE_BUFFER_PAGE_SIZE; + uint32_t end_va_page = va_page + DIV_ROUND_UP(size, ZINK_SPARSE_BUFFER_PAGE_SIZE); + + simple_mtx_lock(&bo->lock); + + if (commit) { + while (va_page < end_va_page) { + uint32_t span_va_page; + + /* Skip pages that are already committed. */ + if (comm[va_page].backing) { + va_page++; + continue; + } + + /* Determine length of uncommitted span. */ + span_va_page = va_page; + while (va_page < end_va_page && !comm[va_page].backing) + va_page++; + + /* Fill the uncommitted span with chunks of backing memory. */ + while (span_va_page < va_page) { + struct zink_sparse_backing *backing; + uint32_t backing_start, backing_size; + + backing_size = va_page - span_va_page; + backing = sparse_backing_alloc(screen, bo, &backing_start, &backing_size); + if (!backing) { + ok = false; + goto out; + } + if (!do_commit_single(screen, res, backing->bo, + (uint64_t)span_va_page * ZINK_SPARSE_BUFFER_PAGE_SIZE, + (uint64_t)backing_size * ZINK_SPARSE_BUFFER_PAGE_SIZE, true)) { + + ok = sparse_backing_free(screen, bo, backing, backing_start, backing_size); + assert(ok && "sufficient memory should already be allocated"); + + ok = false; + goto out; + } + + while (backing_size) { + comm[span_va_page].backing = backing; + comm[span_va_page].page = backing_start; + span_va_page++; + backing_start++; + backing_size--; + } + } + } + } else { + if (!do_commit_single(screen, res, NULL, + (uint64_t)va_page * ZINK_SPARSE_BUFFER_PAGE_SIZE, + (uint64_t)(end_va_page - va_page) * ZINK_SPARSE_BUFFER_PAGE_SIZE, false)) { + ok = false; + goto out; + } + + while (va_page < end_va_page) { + struct zink_sparse_backing *backing; + uint32_t backing_start; + uint32_t span_pages; + + /* Skip pages that are already uncommitted. */ + if (!comm[va_page].backing) { + va_page++; + continue; + } + + /* Group contiguous spans of pages. */ + backing = comm[va_page].backing; + backing_start = comm[va_page].page; + comm[va_page].backing = NULL; + + span_pages = 1; + va_page++; + + while (va_page < end_va_page && + comm[va_page].backing == backing && + comm[va_page].page == backing_start + span_pages) { + comm[va_page].backing = NULL; + va_page++; + span_pages++; + } + + if (!sparse_backing_free(screen, bo, backing, backing_start, span_pages)) { + /* Couldn't allocate tracking data structures, so we have to leak */ + fprintf(stderr, "zink: leaking sparse backing memory\n"); + ok = false; + } + } + } +out: + + simple_mtx_unlock(&bo->lock); + return ok; +} + +static const struct pb_vtbl bo_slab_vtbl = { + /* Cast to void* because one of the function parameters is a struct pointer instead of void*. */ + (void*)bo_slab_destroy + /* other functions are never called */ +}; + +static struct pb_slab * +bo_slab_alloc(void *priv, unsigned heap, unsigned entry_size, unsigned group_index, bool encrypted) +{ + struct zink_screen *screen = priv; + VkMemoryPropertyFlags domains = vk_domain_from_heap(heap); + uint32_t base_id; + unsigned slab_size = 0; + struct zink_slab *slab = CALLOC_STRUCT(zink_slab); + + if (!slab) + return NULL; + + //struct pb_slabs *slabs = ((flags & RADEON_FLAG_ENCRYPTED) && screen->info.has_tmz_support) ? + //screen->bo_slabs_encrypted : screen->bo_slabs; + struct pb_slabs *slabs = screen->pb.bo_slabs; + + /* Determine the slab buffer size. */ + for (unsigned i = 0; i < NUM_SLAB_ALLOCATORS; i++) { + unsigned max_entry_size = 1 << (slabs[i].min_order + slabs[i].num_orders - 1); + + if (entry_size <= max_entry_size) { + /* The slab size is twice the size of the largest possible entry. */ + slab_size = max_entry_size * 2; + + if (!util_is_power_of_two_nonzero(entry_size)) { + assert(util_is_power_of_two_nonzero(entry_size * 4 / 3)); + + /* If the entry size is 3/4 of a power of two, we would waste space and not gain + * anything if we allocated only twice the power of two for the backing buffer: + * 2 * 3/4 = 1.5 usable with buffer size 2 + * + * Allocating 5 times the entry size leads us to the next power of two and results + * in a much better memory utilization: + * 5 * 3/4 = 3.75 usable with buffer size 4 + */ + if (entry_size * 5 > slab_size) + slab_size = util_next_power_of_two(entry_size * 5); + } + + break; + } + } + assert(slab_size != 0); + + slab->buffer = zink_bo(zink_bo_create(screen, slab_size, slab_size, heap, 0)); + if (!slab->buffer) + goto fail; + + slab_size = slab->buffer->base.size; + + slab->base.num_entries = slab_size / entry_size; + slab->base.num_free = slab->base.num_entries; + slab->entry_size = entry_size; + slab->entries = CALLOC(slab->base.num_entries, sizeof(*slab->entries)); + if (!slab->entries) + goto fail_buffer; + + list_inithead(&slab->base.free); + +#ifdef _MSC_VER + /* C11 too hard for msvc, no __sync_fetch_and_add */ + base_id = p_atomic_add_return(&screen->pb.next_bo_unique_id, slab->base.num_entries) - slab->base.num_entries; +#else + base_id = __sync_fetch_and_add(&screen->pb.next_bo_unique_id, slab->base.num_entries); +#endif + for (unsigned i = 0; i < slab->base.num_entries; ++i) { + struct zink_bo *bo = &slab->entries[i]; + + simple_mtx_init(&bo->lock, mtx_plain); + bo->base.alignment_log2 = util_logbase2(get_slab_entry_alignment(screen, entry_size)); + bo->base.size = entry_size; + bo->base.vtbl = &bo_slab_vtbl; + bo->offset = slab->buffer->offset + i * entry_size; + bo->base.placement = domains; + bo->unique_id = base_id + i; + bo->u.slab.entry.slab = &slab->base; + bo->u.slab.entry.group_index = group_index; + bo->u.slab.entry.entry_size = entry_size; + + if (slab->buffer->mem) { + /* The slab is not suballocated. */ + bo->u.slab.real = slab->buffer; + } else { + /* The slab is allocated out of a bigger slab. */ + bo->u.slab.real = slab->buffer->u.slab.real; + assert(bo->u.slab.real->mem); + } + + list_addtail(&bo->u.slab.entry.head, &slab->base.free); + } + + /* Wasted alignment due to slabs with 3/4 allocations being aligned to a power of two. */ + assert(slab->base.num_entries * entry_size <= slab_size); + + return &slab->base; + +fail_buffer: + zink_bo_unref(screen, slab->buffer); +fail: + FREE(slab); + return NULL; +} + +static struct pb_slab * +bo_slab_alloc_normal(void *priv, unsigned heap, unsigned entry_size, unsigned group_index) +{ + return bo_slab_alloc(priv, heap, entry_size, group_index, false); +} + +bool +zink_bo_init(struct zink_screen *screen) +{ + uint64_t total_mem = 0; + for (uint32_t i = 0; i < screen->info.mem_props.memoryHeapCount; ++i) + total_mem += screen->info.mem_props.memoryHeaps[i].size; + /* Create managers. */ + pb_cache_init(&screen->pb.bo_cache, ZINK_HEAP_MAX, + 500000, 2.0f, 0, + total_mem / 8, screen, + (void*)bo_destroy, (void*)bo_can_reclaim); + + unsigned min_slab_order = 8; /* 256 bytes */ + unsigned max_slab_order = 20; /* 1 MB (slab size = 2 MB) */ + unsigned num_slab_orders_per_allocator = (max_slab_order - min_slab_order) / + NUM_SLAB_ALLOCATORS; + + /* Divide the size order range among slab managers. */ + for (unsigned i = 0; i < NUM_SLAB_ALLOCATORS; i++) { + unsigned min_order = min_slab_order; + unsigned max_order = MIN2(min_order + num_slab_orders_per_allocator, + max_slab_order); + + if (!pb_slabs_init(&screen->pb.bo_slabs[i], + min_order, max_order, + ZINK_HEAP_MAX, true, + screen, + bo_can_reclaim_slab, + bo_slab_alloc_normal, + (void*)bo_slab_free)) { + return false; + } + min_slab_order = max_order + 1; + } + screen->pb.min_alloc_size = 1 << screen->pb.bo_slabs[0].min_order; + screen->pb.bo_export_table = util_hash_table_create_ptr_keys(); + simple_mtx_init(&screen->pb.bo_export_table_lock, mtx_plain); + return true; +} + +void +zink_bo_deinit(struct zink_screen *screen) +{ + for (unsigned i = 0; i < NUM_SLAB_ALLOCATORS; i++) { + if (screen->pb.bo_slabs[i].groups) + pb_slabs_deinit(&screen->pb.bo_slabs[i]); + } + pb_cache_deinit(&screen->pb.bo_cache); + _mesa_hash_table_destroy(screen->pb.bo_export_table, NULL); + simple_mtx_destroy(&screen->pb.bo_export_table_lock); +} diff --git a/src/gallium/drivers/zink/zink_bo.h b/src/gallium/drivers/zink/zink_bo.h new file mode 100644 index 00000000000..f189a89ec41 --- /dev/null +++ b/src/gallium/drivers/zink/zink_bo.h @@ -0,0 +1,270 @@ +/* + * Copyright © 2021 Valve Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mike Blumenkrantz <michael.blumenkrantz@gmail.com> + */ + +#ifndef ZINK_BO_H +#define ZINK_BO_H +#include <vulkan/vulkan.h> +#include "pipebuffer/pb_cache.h" +#include "pipebuffer/pb_slab.h" +#include "zink_batch.h" + +#define VK_VIS_VRAM (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) +enum zink_resource_access { + ZINK_RESOURCE_ACCESS_READ = 1, + ZINK_RESOURCE_ACCESS_WRITE = 32, + ZINK_RESOURCE_ACCESS_RW = ZINK_RESOURCE_ACCESS_READ | ZINK_RESOURCE_ACCESS_WRITE, +}; + + +enum zink_heap { + ZINK_HEAP_DEVICE_LOCAL, + ZINK_HEAP_DEVICE_LOCAL_SPARSE, + ZINK_HEAP_DEVICE_LOCAL_VISIBLE, + ZINK_HEAP_HOST_VISIBLE_ANY, + ZINK_HEAP_HOST_VISIBLE_COHERENT, + ZINK_HEAP_HOST_VISIBLE_CACHED, + ZINK_HEAP_MAX, +}; + +enum zink_alloc_flag { + ZINK_ALLOC_SPARSE = 1<<0, + ZINK_ALLOC_NO_SUBALLOC = 1<<1, +}; + + +struct zink_bo { + struct pb_buffer base; + + union { + struct { + void *cpu_ptr; /* for user_ptr and permanent maps */ + int map_count; + + bool is_user_ptr; + bool use_reusable_pool; + + /* Whether buffer_get_handle or buffer_from_handle has been called, + * it can only transition from false to true. Protected by lock. + */ + bool is_shared; + } real; + struct { + struct pb_slab_entry entry; + struct zink_bo *real; + } slab; + struct { + uint32_t num_va_pages; + uint32_t num_backing_pages; + + struct list_head backing; + + /* Commitment information for each page of the virtual memory area. */ + struct zink_sparse_commitment *commitments; + } sparse; + } u; + + VkDeviceMemory mem; + uint64_t offset; + + uint32_t unique_id; + + simple_mtx_t lock; + + struct zink_batch_usage *reads; + struct zink_batch_usage *writes; + + struct pb_cache_entry cache_entry[]; +}; + +static inline struct zink_bo * +zink_bo(struct pb_buffer *pbuf) +{ + return (struct zink_bo*)pbuf; +} + +static inline enum zink_alloc_flag +zink_alloc_flags_from_heap(enum zink_heap heap) +{ + enum zink_alloc_flag flags = 0; + switch (heap) { + case ZINK_HEAP_DEVICE_LOCAL_SPARSE: + flags |= ZINK_ALLOC_SPARSE; + break; + default: + break; + } + return flags; +} + +static inline VkMemoryPropertyFlags +vk_domain_from_heap(enum zink_heap heap) +{ + VkMemoryPropertyFlags domains = 0; + + switch (heap) { + case ZINK_HEAP_DEVICE_LOCAL: + case ZINK_HEAP_DEVICE_LOCAL_SPARSE: + domains = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + break; + case ZINK_HEAP_DEVICE_LOCAL_VISIBLE: + domains = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + break; + case ZINK_HEAP_HOST_VISIBLE_ANY: + domains = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + break; + case ZINK_HEAP_HOST_VISIBLE_COHERENT: + domains = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + case ZINK_HEAP_HOST_VISIBLE_CACHED: + domains = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + break; + default: + break; + } + return domains; +} + +static inline enum zink_heap +zink_heap_from_domain_flags(VkMemoryPropertyFlags domains, enum zink_alloc_flag flags) +{ + if (flags & ZINK_ALLOC_SPARSE) + return ZINK_HEAP_DEVICE_LOCAL_SPARSE; + + if ((domains & VK_VIS_VRAM) == VK_VIS_VRAM) + return ZINK_HEAP_DEVICE_LOCAL_VISIBLE; + + if (domains & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + return ZINK_HEAP_DEVICE_LOCAL; + + if (domains & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) + return ZINK_HEAP_HOST_VISIBLE_COHERENT; + + if (domains & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) + return ZINK_HEAP_HOST_VISIBLE_CACHED; + + return ZINK_HEAP_HOST_VISIBLE_ANY; +} + +bool +zink_bo_init(struct zink_screen *screen); + +void +zink_bo_deinit(struct zink_screen *screen); + +struct pb_buffer * +zink_bo_create(struct zink_screen *screen, uint64_t size, unsigned alignment, enum zink_heap heap, enum zink_alloc_flag flags); + +static inline uint64_t +zink_bo_get_offset(const struct zink_bo *bo) +{ + return bo->offset; +} + +static inline VkDeviceMemory +zink_bo_get_mem(const struct zink_bo *bo) +{ + return bo->mem ? bo->mem : bo->u.slab.real->mem; +} + +static inline VkDeviceSize +zink_bo_get_size(const struct zink_bo *bo) +{ + return bo->mem ? bo->base.size : bo->u.slab.real->base.size; +} + +void * +zink_bo_map(struct zink_screen *screen, struct zink_bo *bo); +void +zink_bo_unmap(struct zink_screen *screen, struct zink_bo *bo); + +bool +zink_bo_commit(struct zink_screen *screen, struct zink_resource *res, uint32_t offset, uint32_t size, bool commit); + +static inline bool +zink_bo_has_unflushed_usage(const struct zink_bo *bo) +{ + return zink_batch_usage_is_unflushed(bo->reads) || + zink_batch_usage_is_unflushed(bo->writes); +} + +static inline bool +zink_bo_has_usage(const struct zink_bo *bo) +{ + return zink_batch_usage_exists(bo->reads) || + zink_batch_usage_exists(bo->writes); +} + +static inline bool +zink_bo_usage_matches(const struct zink_bo *bo, const struct zink_batch_state *bs) +{ + return zink_batch_usage_matches(bo->reads, bs) || + zink_batch_usage_matches(bo->writes, bs); +} + +static inline bool +zink_bo_usage_check_completion(struct zink_screen *screen, struct zink_bo *bo, enum zink_resource_access access) +{ + if (access & ZINK_RESOURCE_ACCESS_READ && !zink_screen_usage_check_completion(screen, bo->reads)) + return false; + if (access & ZINK_RESOURCE_ACCESS_WRITE && !zink_screen_usage_check_completion(screen, bo->writes)) + return false; + return true; +} + +static inline void +zink_bo_usage_wait(struct zink_context *ctx, struct zink_bo *bo, enum zink_resource_access access) +{ + if (access & ZINK_RESOURCE_ACCESS_READ) + zink_batch_usage_wait(ctx, bo->reads); + if (access & ZINK_RESOURCE_ACCESS_WRITE) + zink_batch_usage_wait(ctx, bo->writes); +} + +static inline void +zink_bo_usage_set(struct zink_bo *bo, struct zink_batch_state *bs, bool write) +{ + if (write) + zink_batch_usage_set(&bo->writes, bs); + else + zink_batch_usage_set(&bo->reads, bs); +} + +static inline void +zink_bo_usage_unset(struct zink_bo *bo, struct zink_batch_state *bs) +{ + zink_batch_usage_unset(&bo->reads, bs); + zink_batch_usage_unset(&bo->writes, bs); +} + + +static inline void +zink_bo_unref(struct zink_screen *screen, struct zink_bo *bo) +{ + struct pb_buffer *pbuf = &bo->base; + pb_reference_with_winsys(screen, &pbuf, NULL); +} + +#endif diff --git a/src/gallium/drivers/zink/zink_context.c b/src/gallium/drivers/zink/zink_context.c index 4e5734787d1..b279c3563a1 100644 --- a/src/gallium/drivers/zink/zink_context.c +++ b/src/gallium/drivers/zink/zink_context.c @@ -3201,29 +3201,6 @@ rebind_buffer(struct zink_context *ctx, struct zink_resource *res) zink_batch_resource_usage_set(&ctx->batch, res, has_write); } -static inline struct zink_screen ** -get_screen_ptr_for_commit(uint8_t *mem) -{ - return (struct zink_screen**)(mem + sizeof(VkBindSparseInfo) + sizeof(VkSparseBufferMemoryBindInfo) + sizeof(VkSparseMemoryBind)); -} - -static bool -resource_commit(struct zink_screen *screen, VkBindSparseInfo *sparse) -{ - VkQueue queue = screen->threaded ? screen->thread_queue : screen->queue; - - VkResult ret = vkQueueBindSparse(queue, 1, sparse, VK_NULL_HANDLE); - return zink_screen_handle_vkresult(screen, ret); -} - -static void -submit_resource_commit(void *data, void *gdata, int thread_index) -{ - struct zink_screen **screen = get_screen_ptr_for_commit(data); - resource_commit(*screen, data); - free(data); -} - static bool zink_resource_commit(struct pipe_context *pctx, struct pipe_resource *pres, unsigned level, struct pipe_box *box, bool commit) { @@ -3235,50 +3212,11 @@ zink_resource_commit(struct pipe_context *pctx, struct pipe_resource *pres, unsi if (zink_resource_has_unflushed_usage(res)) zink_flush_queue(ctx); - uint8_t *mem = malloc(sizeof(VkBindSparseInfo) + sizeof(VkSparseBufferMemoryBindInfo) + sizeof(VkSparseMemoryBind) + sizeof(void*)); - if (!mem) - return false; - VkBindSparseInfo *sparse = (void*)mem; - sparse->sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO; - sparse->pNext = NULL; - sparse->waitSemaphoreCount = 0; - sparse->bufferBindCount = 1; - sparse->imageOpaqueBindCount = 0; - sparse->imageBindCount = 0; - sparse->signalSemaphoreCount = 0; - - VkSparseBufferMemoryBindInfo *sparse_bind = (void*)(mem + sizeof(VkBindSparseInfo)); - sparse_bind->buffer = res->obj->buffer; - sparse_bind->bindCount = 1; - sparse->pBufferBinds = sparse_bind; - - VkSparseMemoryBind *mem_bind = (void*)(mem + sizeof(VkBindSparseInfo) + sizeof(VkSparseBufferMemoryBindInfo)); - mem_bind->resourceOffset = box->x; - mem_bind->size = box->width; - mem_bind->memory = commit ? res->obj->mem : VK_NULL_HANDLE; - /* currently sparse buffers allocate memory 1:1 for the max sparse size, - * but probably it should dynamically allocate the committed regions; - * if this ever changes, update the below line - */ - mem_bind->memoryOffset = box->x; - mem_bind->flags = 0; - sparse_bind->pBinds = mem_bind; - - struct zink_screen **ptr = get_screen_ptr_for_commit(mem); - *ptr = screen; + bool ret = zink_bo_commit(screen, res, box->x, box->width, commit); + if (!ret) + check_device_lost(ctx); - if (screen->threaded) { - /* this doesn't need any kind of fencing because any access to this resource - * will be automagically synchronized by queue dispatch */ - util_queue_add_job(&screen->flush_queue, mem, NULL, submit_resource_commit, NULL, 0); - } else { - bool ret = resource_commit(screen, sparse); - if (!ret) - check_device_lost(ctx); - free(sparse); - return ret; - } - return true; + return ret; } static void diff --git a/src/gallium/drivers/zink/zink_resource.c b/src/gallium/drivers/zink/zink_resource.c index 3a706a9fc9c..5080cc13429 100644 --- a/src/gallium/drivers/zink/zink_resource.c +++ b/src/gallium/drivers/zink/zink_resource.c @@ -78,49 +78,6 @@ debug_describe_zink_resource_object(char *buf, const struct zink_resource_object sprintf(buf, "zink_resource_object"); } -static uint32_t -mem_hash(const void *key) -{ - const struct mem_key *mkey = key; - return _mesa_hash_data(&mkey->key, sizeof(mkey->key)); -} - -static bool -mem_equals(const void *a, const void *b) -{ - const struct mem_key *ma = a; - const struct mem_key *mb = b; - return !memcmp(&ma->key, &mb->key, sizeof(ma->key)); -} - -static void -cache_or_free_mem(struct zink_screen *screen, struct zink_resource_object *obj) -{ - if (obj->mkey.key.heap_index != UINT32_MAX) { - simple_mtx_lock(&screen->mem[obj->mkey.key.heap_index].mem_cache_mtx); - struct hash_entry *he = _mesa_hash_table_search_pre_hashed(&screen->mem[obj->mkey.key.heap_index].resource_mem_cache, obj->mem_hash, &obj->mkey); - assert(he); - struct util_dynarray *array = he->data; - struct mem_key *mkey = (void*)he->key; - - unsigned seen = mkey->seen_count; - mkey->seen_count--; - if (util_dynarray_num_elements(array, struct mem_cache_entry) < seen) { - struct mem_cache_entry mc = { obj->mem, obj->map }; - screen->mem[obj->mkey.key.heap_index].mem_cache_size += obj->size; - if (sizeof(void*) == 4 && obj->map) { - vkUnmapMemory(screen->dev, obj->mem); - mc.map = NULL; - } - util_dynarray_append(array, struct mem_cache_entry, mc); - simple_mtx_unlock(&screen->mem[obj->mkey.key.heap_index].mem_cache_mtx); - return; - } - simple_mtx_unlock(&screen->mem[obj->mkey.key.heap_index].mem_cache_mtx); - } - vkFreeMemory(screen->dev, obj->mem, NULL); -} - void zink_destroy_resource_object(struct zink_screen *screen, struct zink_resource_object *obj) { @@ -134,7 +91,10 @@ zink_destroy_resource_object(struct zink_screen *screen, struct zink_resource_ob util_dynarray_fini(&obj->tmp); zink_descriptor_set_refs_clear(&obj->desc_set_refs, obj); - cache_or_free_mem(screen, obj); + if (obj->dedicated) + vkFreeMemory(screen->dev, obj->mem, NULL); + else + zink_bo_unref(screen, obj->bo); FREE(obj); } @@ -155,38 +115,6 @@ zink_resource_destroy(struct pipe_screen *pscreen, FREE(res); } -static uint32_t -get_memory_type_index(struct zink_screen *screen, - const VkMemoryRequirements *reqs, - VkMemoryPropertyFlags props) -{ - int32_t idx = -1; - for (uint32_t i = 0u; i < VK_MAX_MEMORY_TYPES; i++) { - if (((reqs->memoryTypeBits >> i) & 1) == 1) { - if ((screen->info.mem_props.memoryTypes[i].propertyFlags & props) == props) { - if (!(props & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) && - screen->info.mem_props.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { - idx = i; - } else - return i; - } - } - } - if (idx >= 0) - return idx; - - if (props & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { - /* if no suitable cached memory can be found, fall back - * to non-cached memory instead. - */ - return get_memory_type_index(screen, reqs, - props & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT); - } - - unreachable("Unsupported memory-type"); - return 0; -} - static VkImageAspectFlags aspect_from_format(enum pipe_format fmt) { @@ -636,17 +564,31 @@ resource_object_create(struct zink_screen *screen, const struct pipe_resource *t flags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; VkMemoryAllocateInfo mai = {0}; + enum zink_alloc_flag aflags = templ->flags & PIPE_RESOURCE_FLAG_SPARSE ? ZINK_ALLOC_SPARSE : 0; mai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; mai.allocationSize = reqs.size; - mai.memoryTypeIndex = get_memory_type_index(screen, &reqs, flags); + enum zink_heap heap = zink_heap_from_domain_flags(flags, aflags); + mai.memoryTypeIndex = screen->heap_map[heap]; + if (unlikely(!(reqs.memoryTypeBits & BITFIELD_BIT(mai.memoryTypeIndex)))) { + /* not valid based on reqs; demote to more compatible type */ + switch (heap) { + case ZINK_HEAP_DEVICE_LOCAL_VISIBLE: + heap = ZINK_HEAP_DEVICE_LOCAL; + break; + case ZINK_HEAP_HOST_VISIBLE_CACHED: + heap = ZINK_HEAP_HOST_VISIBLE_ANY; + break; + default: + break; + } + mai.memoryTypeIndex = screen->heap_map[heap]; + assert(reqs.memoryTypeBits & BITFIELD_BIT(mai.memoryTypeIndex)); + } VkMemoryType mem_type = screen->info.mem_props.memoryTypes[mai.memoryTypeIndex]; obj->coherent = mem_type.propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; if (!(templ->flags & PIPE_RESOURCE_FLAG_SPARSE)) obj->host_visible = mem_type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; - if (templ->target == PIPE_BUFFER && !obj->coherent && obj->host_visible) { - mai.allocationSize = reqs.size = align(reqs.size, screen->info.props.limits.nonCoherentAtomSize); - } VkMemoryDedicatedAllocateInfo ded_alloc_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, @@ -695,46 +637,33 @@ resource_object_create(struct zink_screen *screen, const struct pipe_resource *t mai.pNext = &memory_wsi_info; } - if (!mai.pNext && !(templ->flags & (PIPE_RESOURCE_FLAG_MAP_COHERENT | PIPE_RESOURCE_FLAG_SPARSE))) { - obj->mkey.key.reqs = reqs; - obj->mkey.key.heap_index = mai.memoryTypeIndex; - obj->mem_hash = mem_hash(&obj->mkey); - simple_mtx_lock(&screen->mem[mai.memoryTypeIndex].mem_cache_mtx); - - struct hash_entry *he = _mesa_hash_table_search_pre_hashed(&screen->mem[mai.memoryTypeIndex].resource_mem_cache, obj->mem_hash, &obj->mkey); - struct mem_key *mkey; - if (he) { - struct util_dynarray *array = he->data; - mkey = (void*)he->key; - if (array && util_dynarray_num_elements(array, struct mem_cache_entry)) { - struct mem_cache_entry mc = util_dynarray_pop(array, struct mem_cache_entry); - obj->mem = mc.mem; - obj->map = mc.map; - screen->mem[mai.memoryTypeIndex].mem_cache_size -= reqs.size; - screen->mem[mai.memoryTypeIndex].mem_cache_count--; - } + if (!mai.pNext) { + unsigned alignment = MAX2(reqs.alignment, 256); + if (templ->usage == PIPE_USAGE_STAGING && obj->is_buffer) + alignment = MAX2(alignment, screen->info.props.limits.minMemoryMapAlignment); + obj->alignment = alignment; + obj->bo = zink_bo(zink_bo_create(screen, reqs.size, alignment, heap, 0)); + if (!obj->bo) + goto fail2; + if (aflags == ZINK_ALLOC_SPARSE) { + obj->size = templ->width0; } else { - mkey = ralloc(screen, struct mem_key); - memcpy(&mkey->key, &obj->mkey.key, sizeof(obj->mkey.key)); - mkey->seen_count = 0; - struct util_dynarray *array = rzalloc(screen, struct util_dynarray); - util_dynarray_init(array, screen); - _mesa_hash_table_insert_pre_hashed(&screen->mem[mai.memoryTypeIndex].resource_mem_cache, obj->mem_hash, mkey, array); + obj->offset = zink_bo_get_offset(obj->bo); + obj->mem = zink_bo_get_mem(obj->bo); + obj->size = zink_bo_get_size(obj->bo); } - mkey->seen_count++; - simple_mtx_unlock(&screen->mem[mai.memoryTypeIndex].mem_cache_mtx); - } else - obj->mkey.key.heap_index = UINT32_MAX; + } else { + obj->dedicated = true; + obj->offset = 0; + obj->size = reqs.size; + } /* TODO: sparse buffers should probably allocate multiple regions of memory instead of giant blobs? */ - if (!obj->mem && vkAllocateMemory(screen->dev, &mai, NULL, &obj->mem) != VK_SUCCESS) { + if (obj->dedicated && vkAllocateMemory(screen->dev, &mai, NULL, &obj->mem) != VK_SUCCESS) { debug_printf("vkAllocateMemory failed\n"); goto fail2; } - obj->offset = 0; - obj->size = reqs.size; - if (templ->target == PIPE_BUFFER) { if (!(templ->flags & PIPE_RESOURCE_FLAG_SPARSE)) if (vkBindBufferMemory(screen->dev, obj->buffer, obj->mem, obj->offset) != VK_SUCCESS) @@ -1109,6 +1038,8 @@ map_resource(struct zink_screen *screen, struct zink_resource *res) if (res->obj->map) return res->obj->map; assert(res->obj->host_visible); + if (!res->obj->dedicated) + return zink_bo_map(screen, res->obj->bo); result = vkMapMemory(screen->dev, res->obj->mem, res->obj->offset, res->obj->size, 0, &res->obj->map); if (zink_screen_handle_vkresult(screen, result)) @@ -1120,7 +1051,10 @@ static void unmap_resource(struct zink_screen *screen, struct zink_resource *res) { res->obj->map = NULL; - vkUnmapMemory(screen->dev, res->obj->mem); + if (!res->obj->dedicated) + zink_bo_unmap(screen, res->obj->bo); + else + vkUnmapMemory(screen->dev, res->obj->mem); } static void * @@ -1672,14 +1606,6 @@ zink_screen_resource_init(struct pipe_screen *pscreen) pscreen->resource_from_handle = zink_resource_from_handle; } pscreen->resource_get_param = zink_resource_get_param; - - screen->mem = rzalloc_array(screen, struct zink_mem_cache, screen->info.mem_props.memoryTypeCount); - if (!screen->mem) - return false; - for (uint32_t i = 0; i < screen->info.mem_props.memoryTypeCount; ++i) { - simple_mtx_init(&screen->mem[i].mem_cache_mtx, mtx_plain); - _mesa_hash_table_init(&screen->mem[i].resource_mem_cache, screen, mem_hash, mem_equals); - } return true; } diff --git a/src/gallium/drivers/zink/zink_resource.h b/src/gallium/drivers/zink/zink_resource.h index 15533fed611..3887f0a308d 100644 --- a/src/gallium/drivers/zink/zink_resource.h +++ b/src/gallium/drivers/zink/zink_resource.h @@ -28,7 +28,7 @@ struct pipe_screen; struct sw_displaytarget; struct zink_batch; struct zink_context; - +struct zink_bo; #define ZINK_RESOURCE_USAGE_STREAMOUT (1 << 10) //much greater than ZINK_DESCRIPTOR_TYPES #include "util/simple_mtx.h" @@ -44,12 +44,6 @@ struct zink_context; #define ZINK_MAP_TEMPORARY (PIPE_MAP_DRV_PRV << 0) -enum zink_resource_access { - ZINK_RESOURCE_ACCESS_READ = 1, - ZINK_RESOURCE_ACCESS_WRITE = 32, - ZINK_RESOURCE_ACCESS_RW = ZINK_RESOURCE_ACCESS_READ | ZINK_RESOURCE_ACCESS_WRITE, -}; - struct mem_key { unsigned seen_count; struct { @@ -70,9 +64,9 @@ struct zink_resource_object { bool transfer_dst; VkImageAspectFlags modifier_aspect; + bool dedicated; + struct zink_bo *bo; VkDeviceMemory mem; - uint32_t mem_hash; - struct mem_key mkey; VkDeviceSize offset, size, alignment; VkSampleLocationsInfoEXT zs_evaluate; @@ -187,75 +181,98 @@ bool zink_resource_object_init_storage(struct zink_context *ctx, struct zink_resource *res); #ifndef __cplusplus +#include "zink_bo.h" static inline bool zink_resource_usage_is_unflushed(const struct zink_resource *res) { - return zink_batch_usage_is_unflushed(res->obj->reads) || - zink_batch_usage_is_unflushed(res->obj->writes); + if (res->obj->dedicated) + return zink_batch_usage_is_unflushed(res->obj->reads) || + zink_batch_usage_is_unflushed(res->obj->writes); + return zink_bo_has_unflushed_usage(res->obj->bo); } static inline bool zink_resource_usage_is_unflushed_write(const struct zink_resource *res) { - return zink_batch_usage_is_unflushed(res->obj->writes); + if (res->obj->dedicated) + return zink_batch_usage_is_unflushed(res->obj->writes); + return zink_batch_usage_is_unflushed(res->obj->bo->writes); } static inline bool zink_resource_usage_matches(const struct zink_resource *res, const struct zink_batch_state *bs) { - return zink_batch_usage_matches(res->obj->reads, bs) || - zink_batch_usage_matches(res->obj->writes, bs); + if (res->obj->dedicated) + return zink_batch_usage_matches(res->obj->reads, bs) || + zink_batch_usage_matches(res->obj->writes, bs); + return zink_bo_usage_matches(res->obj->bo, bs); } static inline bool zink_resource_has_usage(const struct zink_resource *res) { - return zink_batch_usage_exists(res->obj->reads) || - zink_batch_usage_exists(res->obj->writes); + if (res->obj->dedicated) + return zink_batch_usage_exists(res->obj->reads) || + zink_batch_usage_exists(res->obj->writes); + return zink_bo_has_usage(res->obj->bo); } static inline bool zink_resource_has_unflushed_usage(const struct zink_resource *res) { - return zink_batch_usage_is_unflushed(res->obj->reads) || - zink_batch_usage_is_unflushed(res->obj->writes); + if (res->obj->dedicated) + return zink_batch_usage_is_unflushed(res->obj->reads) || + zink_batch_usage_is_unflushed(res->obj->writes); + return zink_bo_has_unflushed_usage(res->obj->bo); } static inline bool zink_resource_usage_check_completion(struct zink_screen *screen, struct zink_resource *res, enum zink_resource_access access) { - if (access & ZINK_RESOURCE_ACCESS_READ && !zink_screen_usage_check_completion(screen, res->obj->reads)) - return false; - if (access & ZINK_RESOURCE_ACCESS_WRITE && !zink_screen_usage_check_completion(screen, res->obj->writes)) - return false; - return true; + if (res->obj->dedicated) { + if (access & ZINK_RESOURCE_ACCESS_READ && !zink_screen_usage_check_completion(screen, res->obj->reads)) + return false; + if (access & ZINK_RESOURCE_ACCESS_WRITE && !zink_screen_usage_check_completion(screen, res->obj->writes)) + return false; + return true; + } + return zink_bo_usage_check_completion(screen, res->obj->bo, access); } static inline void zink_resource_usage_wait(struct zink_context *ctx, struct zink_resource *res, enum zink_resource_access access) { - if (access & ZINK_RESOURCE_ACCESS_READ) - zink_batch_usage_wait(ctx, res->obj->reads); - if (access & ZINK_RESOURCE_ACCESS_WRITE) - zink_batch_usage_wait(ctx, res->obj->writes); + if (res->obj->dedicated) { + if (access & ZINK_RESOURCE_ACCESS_READ) + zink_batch_usage_wait(ctx, res->obj->reads); + if (access & ZINK_RESOURCE_ACCESS_WRITE) + zink_batch_usage_wait(ctx, res->obj->writes); + } else + zink_bo_usage_wait(ctx, res->obj->bo, access); } static inline void zink_resource_usage_set(struct zink_resource *res, struct zink_batch_state *bs, bool write) { - if (write) - zink_batch_usage_set(&res->obj->writes, bs); - else - zink_batch_usage_set(&res->obj->reads, bs); + if (res->obj->dedicated) { + if (write) + zink_batch_usage_set(&res->obj->writes, bs); + else + zink_batch_usage_set(&res->obj->reads, bs); + } else + zink_bo_usage_set(res->obj->bo, bs, write); } static inline void zink_resource_object_usage_unset(struct zink_resource_object *obj, struct zink_batch_state *bs) { - zink_batch_usage_unset(&obj->reads, bs); - zink_batch_usage_unset(&obj->writes, bs); + if (obj->dedicated) { + zink_batch_usage_unset(&obj->reads, bs); + zink_batch_usage_unset(&obj->writes, bs); + } else + zink_bo_usage_unset(obj->bo, bs); } #endif diff --git a/src/gallium/drivers/zink/zink_screen.c b/src/gallium/drivers/zink/zink_screen.c index 16a68195cf7..c6434fa5682 100644 --- a/src/gallium/drivers/zink/zink_screen.c +++ b/src/gallium/drivers/zink/zink_screen.c @@ -585,8 +585,8 @@ zink_get_param(struct pipe_screen *pscreen, enum pipe_cap param) return screen->info.feats.features.shaderCullDistance; case PIPE_CAP_SPARSE_BUFFER_PAGE_SIZE: - /* this is the spec minimum */ - return screen->info.feats.features.sparseBinding ? 64 * 1024 : 0; + + return screen->info.feats.features.sparseBinding ? ZINK_SPARSE_BUFFER_PAGE_SIZE : 0; case PIPE_CAP_VIEWPORT_SUBPIXEL_BITS: return screen->info.props.limits.viewportSubPixelBits; @@ -1038,16 +1038,6 @@ zink_is_format_supported(struct pipe_screen *pscreen, } static void -resource_cache_entry_destroy(struct zink_screen *screen, struct hash_entry *he) -{ - struct util_dynarray *array = (void*)he->data; - util_dynarray_foreach(array, struct mem_cache_entry, mc) { - vkFreeMemory(screen->dev, mc->mem, NULL); - } - util_dynarray_fini(array); -} - -static void zink_destroy_screen(struct pipe_screen *pscreen) { struct zink_screen *screen = zink_screen(pscreen); @@ -1087,15 +1077,7 @@ zink_destroy_screen(struct pipe_screen *pscreen) } #endif disk_cache_destroy(screen->disk_cache); - - for (uint32_t i = 0; i < screen->info.mem_props.memoryHeapCount; ++i) { - simple_mtx_lock(&screen->mem[i].mem_cache_mtx); - hash_table_foreach(&screen->mem[i].resource_mem_cache, he) - resource_cache_entry_destroy(screen, he); - simple_mtx_unlock(&screen->mem[i].mem_cache_mtx); - simple_mtx_destroy(&screen->mem[i].mem_cache_mtx); - } - + zink_bo_deinit(screen); util_live_shader_cache_deinit(&screen->shaders); if (screen->sem) @@ -1892,6 +1874,7 @@ zink_internal_create_screen(const struct pipe_screen_config *config) if (!zink_screen_resource_init(&screen->base)) goto fail; + zink_bo_init(screen); zink_screen_fence_init(&screen->base); zink_screen_init_compiler(screen); @@ -1921,6 +1904,25 @@ zink_internal_create_screen(const struct pipe_screen_config *config) if (screen->info.have_KHR_timeline_semaphore) zink_screen_init_semaphore(screen); + memset(&screen->heap_map, UINT8_MAX, sizeof(screen->heap_map)); + for (enum zink_heap i = 0; i < ZINK_HEAP_MAX; i++) { + for (unsigned j = 0; j < screen->info.mem_props.memoryTypeCount; j++) { + VkMemoryPropertyFlags domains = vk_domain_from_heap(i); + if ((screen->info.mem_props.memoryTypes[j].propertyFlags & domains) == domains) { + assert(screen->heap_map[i] == UINT8_MAX); + screen->heap_map[i] = j; + break; + } + } + + /* not found: use compatible heap */ + if (screen->heap_map[i] == UINT8_MAX) { + /* only cached mem has a failure case for now */ + assert(i == ZINK_HEAP_HOST_VISIBLE_CACHED); + screen->heap_map[i] = screen->heap_map[ZINK_HEAP_HOST_VISIBLE_ANY]; + } + } + simple_mtx_init(&screen->surface_mtx, mtx_plain); simple_mtx_init(&screen->bufferview_mtx, mtx_plain); simple_mtx_init(&screen->framebuffer_mtx, mtx_plain); diff --git a/src/gallium/drivers/zink/zink_screen.h b/src/gallium/drivers/zink/zink_screen.h index e74c514b336..082ac2d23a6 100644 --- a/src/gallium/drivers/zink/zink_screen.h +++ b/src/gallium/drivers/zink/zink_screen.h @@ -37,7 +37,8 @@ #include "util/simple_mtx.h" #include "util/u_queue.h" #include "util/u_live_shader_cache.h" - +#include "pipebuffer/pb_cache.h" +#include "pipebuffer/pb_slab.h" #include <vulkan/vulkan.h> extern uint32_t zink_debug; @@ -50,11 +51,16 @@ struct zink_program; struct zink_shader; enum zink_descriptor_type; +/* this is the spec minimum */ +#define ZINK_SPARSE_BUFFER_PAGE_SIZE (64 * 1024) + #define ZINK_DEBUG_NIR 0x1 #define ZINK_DEBUG_SPIRV 0x2 #define ZINK_DEBUG_TGSI 0x4 #define ZINK_DEBUG_VALIDATION 0x8 +#define NUM_SLAB_ALLOCATORS 3 + enum zink_descriptor_mode { ZINK_DESCRIPTOR_MODE_AUTO, ZINK_DESCRIPTOR_MODE_LAZY, @@ -66,13 +72,6 @@ struct zink_modifier_prop { VkDrmFormatModifierPropertiesEXT* pDrmFormatModifierProperties; }; -struct zink_mem_cache { - simple_mtx_t mem_cache_mtx; - struct hash_table resource_mem_cache; - uint64_t mem_cache_size; - unsigned mem_cache_count; -}; - struct zink_screen { struct pipe_screen base; bool threaded; @@ -99,7 +98,15 @@ struct zink_screen { struct util_live_shader_cache shaders; - struct zink_mem_cache *mem; + struct { + struct pb_cache bo_cache; + struct pb_slabs bo_slabs[NUM_SLAB_ALLOCATORS]; + unsigned min_alloc_size; + struct hash_table *bo_export_table; + simple_mtx_t bo_export_table_lock; + uint32_t next_bo_unique_id; + } pb; + uint8_t heap_map[VK_MAX_MEMORY_TYPES]; uint64_t total_video_mem; uint64_t clamp_video_mem; |