mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-04-13 08:22:16 -04:00
render/vulkan: Better bucket size selection
The current buffer allocator allocates a buffer that is either the minimum stage span, the requested size times two, or the largest current allocation times two. Imagine needing a miniscule allocation. We currently have 1M, 8M and 16M buffers, but the 1M buffer is full or not available. The last rule would cause us to request a 32M buffer, rather than a more appropriate 2M buffer to fill the bucket sequence. Make two adjustments to this logic: 1. Round the requested size up to the nearest power of two to avoid odd bucket sizes. 2. Look through the available buffers and find a hole in the bucket sequence to fill, which is made easy by the power of two rule above as we can just iterate until the buffer we are looking at is more than 2x our current target. The buffer we create is inserted into the middle of the list of buffers as needed to maintain the size order.
This commit is contained in:
parent
6dcab66c73
commit
e180bedd61
1 changed files with 36 additions and 11 deletions
|
|
@ -197,6 +197,25 @@ static void shared_buffer_destroy(struct wlr_vk_renderer *r,
|
|||
free(buffer);
|
||||
}
|
||||
|
||||
static VkDeviceSize minimum_bucket(VkDeviceSize size) {
|
||||
// Require at least 2x space in the bucket
|
||||
size *= 2;
|
||||
|
||||
if (size < min_stage_size) {
|
||||
return min_stage_size;
|
||||
}
|
||||
|
||||
// Calculate the smallest containing power of two
|
||||
size -= 1;
|
||||
size |= size >> 1;
|
||||
size |= size >> 2;
|
||||
size |= size >> 4;
|
||||
size |= size >> 8;
|
||||
size |= size >> 16;
|
||||
size |= size >> 32;
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
struct wlr_vk_buffer_span vulkan_get_stage_span(struct wlr_vk_renderer *r,
|
||||
VkDeviceSize size, VkDeviceSize alignment) {
|
||||
// try to find free span
|
||||
|
|
@ -244,16 +263,22 @@ struct wlr_vk_buffer_span vulkan_get_stage_span(struct wlr_vk_renderer *r,
|
|||
goto error_alloc;
|
||||
}
|
||||
|
||||
// we didn't find a free buffer - create one
|
||||
// size = clamp(max(size * 2, prev_size * 2), min_size, max_size)
|
||||
VkDeviceSize bsize = size * 2;
|
||||
bsize = bsize < min_stage_size ? min_stage_size : bsize;
|
||||
if (!wl_list_empty(&r->stage.buffers)) {
|
||||
struct wl_list *last_link = r->stage.buffers.prev;
|
||||
struct wlr_vk_shared_buffer *prev = wl_container_of(
|
||||
last_link, prev, link);
|
||||
VkDeviceSize last_size = 2 * prev->buf_size;
|
||||
bsize = bsize < last_size ? last_size : bsize;
|
||||
// We allocate buffers in buckets of increasing size, each twice the
|
||||
// previous and always able to hold at least 2x the allocation request.
|
||||
VkDeviceSize bsize = minimum_bucket(size);
|
||||
struct wl_list *insertion_target = NULL;
|
||||
wl_list_for_each_reverse(buf, &r->stage.buffers, link) {
|
||||
if (buf->buf_size >= bsize) {
|
||||
if (buf->buf_size > bsize * 2) {
|
||||
// There have found a missing bucket size in the sequence.
|
||||
insertion_target = &buf->link;
|
||||
break;
|
||||
}
|
||||
bsize *= 2;
|
||||
}
|
||||
}
|
||||
if (insertion_target == NULL) {
|
||||
insertion_target = &r->stage.buffers;
|
||||
}
|
||||
|
||||
if (bsize > max_stage_size) {
|
||||
|
|
@ -325,7 +350,7 @@ struct wlr_vk_buffer_span vulkan_get_stage_span(struct wlr_vk_renderer *r,
|
|||
}
|
||||
|
||||
buf->buf_size = bsize;
|
||||
wl_list_insert(&r->stage.buffers, &buf->link);
|
||||
wl_list_insert(insertion_target, &buf->link);
|
||||
|
||||
*a = (struct wlr_vk_allocation){
|
||||
.start = 0,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue