anv: Add a helper for doing mass allocations

We tend to try to reduce the number of allocation calls the Vulkan
driver uses by doing a single allocation whenever possible for a data
structure.  While this has certain downsides (usually code complexity),
it does mean error handling and cleanup is much easier.  This commit
adds a nice little helper struct for getting rid of some of that
complexity.

Reviewed-by: Nanley Chery <nanley.g.chery@intel.com>
This commit is contained in:
Jason Ekstrand 2017-04-06 22:15:16 -07:00
parent 82695c32b6
commit e5c29b8c27
1 changed files with 96 additions and 0 deletions

View File

@ -269,6 +269,102 @@ void anv_loge_v(const char *format, va_list va);
#define anv_assert(x)
#endif
/* A multi-pointer allocator
*
* When copying data structures from the user (such as a render pass), it's
* common to need to allocate data for a bunch of different things. Instead
* of doing several allocations and having to handle all of the error checking
* that entails, it can be easier to do a single allocation. This struct
* helps facilitate that. The intended usage looks like this:
*
* ANV_MULTIALLOC(ma)
* anv_multialloc_add(&ma, &main_ptr, 1);
* anv_multialloc_add(&ma, &substruct1, substruct1Count);
* anv_multialloc_add(&ma, &substruct2, substruct2Count);
*
* if (!anv_multialloc_alloc(&ma, pAllocator, VK_ALLOCATION_SCOPE_FOO))
* return vk_error(VK_ERROR_OUT_OF_HOST_MEORY);
*/
struct anv_multialloc {
size_t size;
size_t align;
uint32_t ptr_count;
void **ptrs[8];
};
#define ANV_MULTIALLOC_INIT \
((struct anv_multialloc) { 0, })
#define ANV_MULTIALLOC(_name) \
struct anv_multialloc _name = ANV_MULTIALLOC_INIT
__attribute__((always_inline))
static inline void
_anv_multialloc_add(struct anv_multialloc *ma,
void **ptr, size_t size, size_t align)
{
size_t offset = align_u64(ma->size, align);
ma->size = offset + size;
ma->align = MAX2(ma->align, align);
/* Store the offset in the pointer. */
*ptr = (void *)(uintptr_t)offset;
assert(ma->ptr_count < ARRAY_SIZE(ma->ptrs));
ma->ptrs[ma->ptr_count++] = ptr;
}
#define anv_multialloc_add(_ma, _ptr, _count) \
_anv_multialloc_add((_ma), (void **)(_ptr), \
(_count) * sizeof(**(_ptr)), __alignof__(**(_ptr)))
__attribute__((always_inline))
static inline void *
anv_multialloc_alloc(struct anv_multialloc *ma,
const VkAllocationCallbacks *alloc,
VkSystemAllocationScope scope)
{
void *ptr = vk_alloc(alloc, ma->size, ma->align, scope);
if (!ptr)
return NULL;
/* Fill out each of the pointers with their final value.
*
* for (uint32_t i = 0; i < ma->ptr_count; i++)
* *ma->ptrs[i] = ptr + (uintptr_t)*ma->ptrs[i];
*
* Unfortunately, even though ma->ptr_count is basically guaranteed to be a
* constant, GCC is incapable of figuring this out and unrolling the loop
* so we have to give it a little help.
*/
STATIC_ASSERT(ARRAY_SIZE(ma->ptrs) == 8);
#define _ANV_MULTIALLOC_UPDATE_POINTER(_i) \
if ((_i) < ma->ptr_count) \
*ma->ptrs[_i] = ptr + (uintptr_t)*ma->ptrs[_i]
_ANV_MULTIALLOC_UPDATE_POINTER(0);
_ANV_MULTIALLOC_UPDATE_POINTER(1);
_ANV_MULTIALLOC_UPDATE_POINTER(2);
_ANV_MULTIALLOC_UPDATE_POINTER(3);
_ANV_MULTIALLOC_UPDATE_POINTER(4);
_ANV_MULTIALLOC_UPDATE_POINTER(5);
_ANV_MULTIALLOC_UPDATE_POINTER(6);
_ANV_MULTIALLOC_UPDATE_POINTER(7);
#undef _ANV_MULTIALLOC_UPDATE_POINTER
return ptr;
}
__attribute__((always_inline))
static inline void *
anv_multialloc_alloc2(struct anv_multialloc *ma,
const VkAllocationCallbacks *parent_alloc,
const VkAllocationCallbacks *alloc,
VkSystemAllocationScope scope)
{
return anv_multialloc_alloc(ma, alloc ? alloc : parent_alloc, scope);
}
/**
* A dynamically growable, circular buffer. Elements are added at head and
* removed from tail. head and tail are free-running uint32_t indices and we