zink: add docs for zink_batch.c

Acked-by: Erik Faye-Lund <erik.faye-lund@collabora.com>
Reviewed-by: Adam Jackson <ajax@redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/19165>
This commit is contained in:
Mike Blumenkrantz
2022-10-19 08:05:48 -04:00
committed by Marge Bot
parent 8bf1aa1b76
commit eef575b91d

View File

@@ -22,14 +22,18 @@ debug_describe_zink_batch_state(char *buf, const struct zink_batch_state *ptr)
sprintf(buf, "zink_batch_state"); sprintf(buf, "zink_batch_state");
} }
/* this resets the batch usage and tracking for a resource object */
static void static void
reset_obj(struct zink_screen *screen, struct zink_batch_state *bs, struct zink_resource_object *obj) reset_obj(struct zink_screen *screen, struct zink_batch_state *bs, struct zink_resource_object *obj)
{ {
/* if no batch usage exists after removing the usage from 'bs', this resource is considered fully idle */
if (!zink_resource_object_usage_unset(obj, bs)) { if (!zink_resource_object_usage_unset(obj, bs)) {
/* the resource is idle, so reset all access/reordering info */
obj->unordered_read = false; obj->unordered_read = false;
obj->unordered_write = false; obj->unordered_write = false;
obj->access = 0; obj->access = 0;
obj->access_stage = 0; obj->access_stage = 0;
/* also prune dead view objects */
simple_mtx_lock(&obj->view_lock); simple_mtx_lock(&obj->view_lock);
if (obj->is_buffer) { if (obj->is_buffer) {
while (util_dynarray_contains(&obj->views, VkBufferView)) while (util_dynarray_contains(&obj->views, VkBufferView))
@@ -54,9 +58,14 @@ reset_obj(struct zink_screen *screen, struct zink_batch_state *bs, struct zink_r
} }
simple_mtx_unlock(&obj->view_lock); simple_mtx_unlock(&obj->view_lock);
} }
/* resource objects are not unrefed here;
* this is typically the last ref on a resource object, and destruction will
* usually trigger an ioctl, so defer deletion to the submit thread to avoid blocking
*/
util_dynarray_append(&bs->unref_resources, struct zink_resource_object*, obj); util_dynarray_append(&bs->unref_resources, struct zink_resource_object*, obj);
} }
/* reset all the resource objects in a given batch object list */
static void static void
reset_obj_list(struct zink_screen *screen, struct zink_batch_state *bs, struct zink_batch_obj_list *list) reset_obj_list(struct zink_screen *screen, struct zink_batch_state *bs, struct zink_batch_obj_list *list)
{ {
@@ -65,6 +74,7 @@ reset_obj_list(struct zink_screen *screen, struct zink_batch_state *bs, struct z
list->num_buffers = 0; list->num_buffers = 0;
} }
/* reset a given batch state */
void void
zink_reset_batch_state(struct zink_context *ctx, struct zink_batch_state *bs) zink_reset_batch_state(struct zink_context *ctx, struct zink_batch_state *bs)
{ {
@@ -74,7 +84,7 @@ zink_reset_batch_state(struct zink_context *ctx, struct zink_batch_state *bs)
if (result != VK_SUCCESS) if (result != VK_SUCCESS)
mesa_loge("ZINK: vkResetCommandPool failed (%s)", vk_Result_to_str(result)); mesa_loge("ZINK: vkResetCommandPool failed (%s)", vk_Result_to_str(result));
/* unref all used resources */ /* unref/reset all used resources */
reset_obj_list(screen, bs, &bs->real_objs); reset_obj_list(screen, bs, &bs->real_objs);
reset_obj_list(screen, bs, &bs->slab_objs); reset_obj_list(screen, bs, &bs->slab_objs);
reset_obj_list(screen, bs, &bs->sparse_objs); reset_obj_list(screen, bs, &bs->sparse_objs);
@@ -83,6 +93,7 @@ zink_reset_batch_state(struct zink_context *ctx, struct zink_batch_state *bs)
reset_obj(screen, bs, obj); reset_obj(screen, bs, obj);
} }
/* this is where bindless texture/buffer ids get recycled */
for (unsigned i = 0; i < 2; i++) { for (unsigned i = 0; i < 2; i++) {
while (util_dynarray_contains(&bs->bindless_releases[i], uint32_t)) { while (util_dynarray_contains(&bs->bindless_releases[i], uint32_t)) {
uint32_t handle = util_dynarray_pop(&bs->bindless_releases[i], uint32_t); uint32_t handle = util_dynarray_pop(&bs->bindless_releases[i], uint32_t);
@@ -92,15 +103,22 @@ zink_reset_batch_state(struct zink_context *ctx, struct zink_batch_state *bs)
} }
} }
/* queries must only be destroyed once they are inactive */
set_foreach_remove(&bs->active_queries, entry) { set_foreach_remove(&bs->active_queries, entry) {
struct zink_query *query = (void*)entry->key; struct zink_query *query = (void*)entry->key;
zink_prune_query(screen, bs, query); zink_prune_query(screen, bs, query);
} }
/* framebuffers are appended to the batch state in which they are destroyed
* to ensure deferred deletion without destroying in-use objects
*/
util_dynarray_foreach(&bs->dead_framebuffers, struct zink_framebuffer*, fb) { util_dynarray_foreach(&bs->dead_framebuffers, struct zink_framebuffer*, fb) {
zink_framebuffer_reference(screen, fb, NULL); zink_framebuffer_reference(screen, fb, NULL);
} }
util_dynarray_clear(&bs->dead_framebuffers); util_dynarray_clear(&bs->dead_framebuffers);
/* samplers are appended to the batch state in which they are destroyed
* to ensure deferred deletion without destroying in-use objects
*/
util_dynarray_foreach(&bs->zombie_samplers, VkSampler, samp) { util_dynarray_foreach(&bs->zombie_samplers, VkSampler, samp) {
VKSCR(DestroySampler)(screen->dev, *samp, NULL); VKSCR(DestroySampler)(screen->dev, *samp, NULL);
} }
@@ -109,6 +127,7 @@ zink_reset_batch_state(struct zink_context *ctx, struct zink_batch_state *bs)
zink_batch_descriptor_reset(screen, bs); zink_batch_descriptor_reset(screen, bs);
/* programs are refcounted and batch-tracked */
set_foreach_remove(&bs->programs, entry) { set_foreach_remove(&bs->programs, entry) {
struct zink_program *pg = (struct zink_program*)entry->key; struct zink_program *pg = (struct zink_program*)entry->key;
zink_batch_usage_unset(&pg->batch_uses, bs); zink_batch_usage_unset(&pg->batch_uses, bs);
@@ -120,6 +139,9 @@ zink_reset_batch_state(struct zink_context *ctx, struct zink_batch_state *bs)
util_dynarray_clear(&bs->wait_semaphore_stages); util_dynarray_clear(&bs->wait_semaphore_stages);
bs->present = VK_NULL_HANDLE; bs->present = VK_NULL_HANDLE;
/* semaphores are not destroyed here;
* destroying semaphores triggers ioctls, so defer deletion to the submit thread to avoid blocking
*/
memcpy(&bs->unref_semaphores, &bs->acquires, sizeof(struct util_dynarray)); memcpy(&bs->unref_semaphores, &bs->acquires, sizeof(struct util_dynarray));
util_dynarray_init(&bs->acquires, NULL); util_dynarray_init(&bs->acquires, NULL);
while (util_dynarray_contains(&bs->wait_semaphores, VkSemaphore)) while (util_dynarray_contains(&bs->wait_semaphores, VkSemaphore))
@@ -127,6 +149,7 @@ zink_reset_batch_state(struct zink_context *ctx, struct zink_batch_state *bs)
util_dynarray_init(&bs->wait_semaphores, NULL); util_dynarray_init(&bs->wait_semaphores, NULL);
bs->swapchain = NULL; bs->swapchain = NULL;
/* swapchain views are managed independent of the owner resource */
while (util_dynarray_contains(&bs->dead_swapchains, VkImageView)) while (util_dynarray_contains(&bs->dead_swapchains, VkImageView))
VKSCR(DestroyImageView)(screen->dev, util_dynarray_pop(&bs->dead_swapchains, VkImageView), NULL); VKSCR(DestroyImageView)(screen->dev, util_dynarray_pop(&bs->dead_swapchains, VkImageView), NULL);
@@ -144,11 +167,13 @@ zink_reset_batch_state(struct zink_context *ctx, struct zink_batch_state *bs)
bs->last_added_obj = NULL; bs->last_added_obj = NULL;
} }
/* this is where deferred resource unrefs occur */
static void static void
unref_resources(struct zink_screen *screen, struct zink_batch_state *bs) unref_resources(struct zink_screen *screen, struct zink_batch_state *bs)
{ {
while (util_dynarray_contains(&bs->unref_resources, struct zink_resource_object*)) { while (util_dynarray_contains(&bs->unref_resources, struct zink_resource_object*)) {
struct zink_resource_object *obj = util_dynarray_pop(&bs->unref_resources, struct zink_resource_object*); struct zink_resource_object *obj = util_dynarray_pop(&bs->unref_resources, struct zink_resource_object*);
/* view pruning may be deferred to avoid ballooning */
if (obj->view_prune_timeline && zink_screen_check_last_finished(screen, obj->view_prune_timeline)) { if (obj->view_prune_timeline && zink_screen_check_last_finished(screen, obj->view_prune_timeline)) {
simple_mtx_lock(&obj->view_lock); simple_mtx_lock(&obj->view_lock);
/* check again under lock in case multi-context use is in the same place */ /* check again under lock in case multi-context use is in the same place */
@@ -174,12 +199,14 @@ unref_resources(struct zink_screen *screen, struct zink_batch_state *bs)
} }
simple_mtx_unlock(&obj->view_lock); simple_mtx_unlock(&obj->view_lock);
} }
/* this is typically where resource objects get destroyed */
zink_resource_object_reference(screen, &obj, NULL); zink_resource_object_reference(screen, &obj, NULL);
} }
while (util_dynarray_contains(&bs->unref_semaphores, VkSemaphore)) while (util_dynarray_contains(&bs->unref_semaphores, VkSemaphore))
VKSCR(DestroySemaphore)(screen->dev, util_dynarray_pop(&bs->unref_semaphores, VkSemaphore), NULL); VKSCR(DestroySemaphore)(screen->dev, util_dynarray_pop(&bs->unref_semaphores, VkSemaphore), NULL);
} }
/* utility for resetting a batch state; called on context destruction */
void void
zink_clear_batch_state(struct zink_context *ctx, struct zink_batch_state *bs) zink_clear_batch_state(struct zink_context *ctx, struct zink_batch_state *bs)
{ {
@@ -188,6 +215,7 @@ zink_clear_batch_state(struct zink_context *ctx, struct zink_batch_state *bs)
unref_resources(zink_screen(ctx->base.screen), bs); unref_resources(zink_screen(ctx->base.screen), bs);
} }
/* utility for managing the singly-linked batch state list */
static void static void
pop_batch_state(struct zink_context *ctx) pop_batch_state(struct zink_context *ctx)
{ {
@@ -198,6 +226,9 @@ pop_batch_state(struct zink_context *ctx)
ctx->last_fence = NULL; ctx->last_fence = NULL;
} }
/* reset all batch states and append to the free state list
* only usable after a full stall
*/
void void
zink_batch_reset_all(struct zink_context *ctx) zink_batch_reset_all(struct zink_context *ctx)
{ {
@@ -214,6 +245,7 @@ zink_batch_reset_all(struct zink_context *ctx)
} }
} }
/* called only on context destruction */
void void
zink_batch_state_destroy(struct zink_screen *screen, struct zink_batch_state *bs) zink_batch_state_destroy(struct zink_screen *screen, struct zink_batch_state *bs)
{ {
@@ -248,6 +280,10 @@ zink_batch_state_destroy(struct zink_screen *screen, struct zink_batch_state *bs
ralloc_free(bs); ralloc_free(bs);
} }
/* batch states are created:
* - on context creation
* - dynamically up to a threshold if no free ones are available
*/
static struct zink_batch_state * static struct zink_batch_state *
create_batch_state(struct zink_context *ctx) create_batch_state(struct zink_context *ctx)
{ {
@@ -317,6 +353,7 @@ fail:
return NULL; return NULL;
} }
/* a batch state is considered "free" if it is both submitted and completed */
static inline bool static inline bool
find_unused_state(struct zink_batch_state *bs) find_unused_state(struct zink_batch_state *bs)
{ {
@@ -327,12 +364,14 @@ find_unused_state(struct zink_batch_state *bs)
return submitted && completed; return submitted && completed;
} }
/* find a "free" batch state */
static struct zink_batch_state * static struct zink_batch_state *
get_batch_state(struct zink_context *ctx, struct zink_batch *batch) get_batch_state(struct zink_context *ctx, struct zink_batch *batch)
{ {
struct zink_screen *screen = zink_screen(ctx->base.screen); struct zink_screen *screen = zink_screen(ctx->base.screen);
struct zink_batch_state *bs = NULL; struct zink_batch_state *bs = NULL;
/* try from the ones that are known to be free first */
if (ctx->free_batch_states) { if (ctx->free_batch_states) {
bs = ctx->free_batch_states; bs = ctx->free_batch_states;
ctx->free_batch_states = bs->next; ctx->free_batch_states = bs->next;
@@ -361,11 +400,13 @@ get_batch_state(struct zink_context *ctx, struct zink_batch *batch)
ctx->last_free_batch_state = state; ctx->last_free_batch_state = state;
} }
} }
/* no batch states were available: make a new one */
bs = create_batch_state(ctx); bs = create_batch_state(ctx);
} }
return bs; return bs;
} }
/* reset the batch object: get a new state and unset 'has_work' to disable flushing */
void void
zink_reset_batch(struct zink_context *ctx, struct zink_batch *batch) zink_reset_batch(struct zink_context *ctx, struct zink_batch *batch)
{ {
@@ -375,6 +416,7 @@ zink_reset_batch(struct zink_context *ctx, struct zink_batch *batch)
batch->has_work = false; batch->has_work = false;
} }
/* called on context creation and after flushing an old batch */
void void
zink_start_batch(struct zink_context *ctx, struct zink_batch *batch) zink_start_batch(struct zink_context *ctx, struct zink_batch *batch)
{ {
@@ -404,6 +446,7 @@ zink_start_batch(struct zink_context *ctx, struct zink_batch *batch)
zink_resume_queries(ctx, batch); zink_resume_queries(ctx, batch);
} }
/* common operations to run post submit; split out for clarity */
static void static void
post_submit(void *data, void *gdata, int thread_index) post_submit(void *data, void *gdata, int thread_index)
{ {
@@ -418,8 +461,10 @@ post_submit(void *data, void *gdata, int thread_index)
abort(); abort();
screen->device_lost = true; screen->device_lost = true;
} else if (bs->ctx->batch_states_count > 5000) { } else if (bs->ctx->batch_states_count > 5000) {
/* throttle in case something crazy is happening */
zink_screen_timeline_wait(screen, bs->fence.batch_id - 2500, PIPE_TIMEOUT_INFINITE); zink_screen_timeline_wait(screen, bs->fence.batch_id - 2500, PIPE_TIMEOUT_INFINITE);
} }
/* this resets the buffer hashlist for the state's next use */
memset(&bs->buffer_indices_hashlist, -1, sizeof(bs->buffer_indices_hashlist)); memset(&bs->buffer_indices_hashlist, -1, sizeof(bs->buffer_indices_hashlist));
} }
@@ -519,6 +564,7 @@ end:
unref_resources(screen, bs); unref_resources(screen, bs);
} }
/* called during flush */
void void
zink_end_batch(struct zink_context *ctx, struct zink_batch *batch) zink_end_batch(struct zink_context *ctx, struct zink_batch *batch)
{ {
@@ -530,6 +576,7 @@ zink_end_batch(struct zink_context *ctx, struct zink_batch *batch)
struct zink_screen *screen = zink_screen(ctx->base.screen); struct zink_screen *screen = zink_screen(ctx->base.screen);
struct zink_batch_state *bs; struct zink_batch_state *bs;
/* oom flushing is triggered to handle stupid piglit tests like streaming-texture-leak */
if (ctx->oom_flush || ctx->batch_states_count > 25) { if (ctx->oom_flush || ctx->batch_states_count > 25) {
assert(!ctx->batch_states_count || ctx->batch_states); assert(!ctx->batch_states_count || ctx->batch_states);
while (ctx->batch_states) { while (ctx->batch_states) {
@@ -562,6 +609,7 @@ zink_end_batch(struct zink_context *ctx, struct zink_batch *batch)
ctx->batch_states_count++; ctx->batch_states_count++;
batch->work_count = 0; batch->work_count = 0;
/* this is swapchain presentation semaphore handling */
if (batch->swapchain) { if (batch->swapchain) {
if (zink_kopper_acquired(batch->swapchain->obj->dt, batch->swapchain->obj->dt_idx) && !batch->swapchain->obj->present) { if (zink_kopper_acquired(batch->swapchain->obj->dt, batch->swapchain->obj->dt_idx) && !batch->swapchain->obj->present) {
batch->state->present = zink_kopper_present(screen, batch->swapchain); batch->state->present = zink_kopper_present(screen, batch->swapchain);
@@ -637,6 +685,7 @@ batch_ptr_add_usage(struct zink_batch *batch, struct set *s, void *ptr)
return !found; return !found;
} }
/* this is a vague, handwave-y estimate */
ALWAYS_INLINE static void ALWAYS_INLINE static void
check_oom_flush(struct zink_context *ctx, const struct zink_batch *batch) check_oom_flush(struct zink_context *ctx, const struct zink_batch *batch)
{ {
@@ -647,6 +696,7 @@ check_oom_flush(struct zink_context *ctx, const struct zink_batch *batch)
} }
} }
/* this adds a ref (batch tracking) */
void void
zink_batch_reference_resource(struct zink_batch *batch, struct zink_resource *res) zink_batch_reference_resource(struct zink_batch *batch, struct zink_resource *res)
{ {
@@ -654,11 +704,13 @@ zink_batch_reference_resource(struct zink_batch *batch, struct zink_resource *re
zink_resource_object_reference(NULL, NULL, res->obj); zink_resource_object_reference(NULL, NULL, res->obj);
} }
/* this adds batch usage */
bool bool
zink_batch_reference_resource_move(struct zink_batch *batch, struct zink_resource *res) zink_batch_reference_resource_move(struct zink_batch *batch, struct zink_resource *res)
{ {
struct zink_batch_state *bs = batch->state; struct zink_batch_state *bs = batch->state;
/* swapchains are special */
if (zink_is_swapchain(res)) { if (zink_is_swapchain(res)) {
struct zink_resource_object **swapchains = bs->swapchain_obj.data; struct zink_resource_object **swapchains = bs->swapchain_obj.data;
unsigned count = util_dynarray_num_elements(&bs->swapchain_obj, struct zink_resource_object*); unsigned count = util_dynarray_num_elements(&bs->swapchain_obj, struct zink_resource_object*);
@@ -717,6 +769,7 @@ zink_batch_reference_resource_move(struct zink_batch *batch, struct zink_resourc
return false; return false;
} }
/* this is how programs achieve deferred deletion */
void void
zink_batch_reference_program(struct zink_batch *batch, zink_batch_reference_program(struct zink_batch *batch,
struct zink_program *pg) struct zink_program *pg)
@@ -729,6 +782,7 @@ zink_batch_reference_program(struct zink_batch *batch,
batch->has_work = true; batch->has_work = true;
} }
/* a fast (hopefully) way to check whether a given batch has completed */
bool bool
zink_screen_usage_check_completion(struct zink_screen *screen, const struct zink_batch_usage *u) zink_screen_usage_check_completion(struct zink_screen *screen, const struct zink_batch_usage *u)
{ {