From 8dd314d2035876c62292f633819105939c3f6933 Mon Sep 17 00:00:00 2001 From: Mike Blumenkrantz Date: Wed, 30 Jun 2021 14:35:02 -0400 Subject: [PATCH] zink: handle broken resource mapping deadlocks some apps (most notably Wolfenstein: The New Order) have broken multi-context buffer usage in which one context will attempt to write to a buffer while another context holds unflushed usage, and the unflushed context will never flush until the buffer write completes it's impossible to handle this scenario correctly without deadlocking, so add some handling to try waiting and then yolo the buffer write if a deadlock would occur Part-of: --- src/gallium/drivers/zink/zink_batch.c | 22 +++++++++++++++++++--- src/gallium/drivers/zink/zink_batch.h | 3 +++ src/gallium/drivers/zink/zink_bo.h | 9 +++++++++ src/gallium/drivers/zink/zink_resource.c | 10 ++++++++-- src/gallium/drivers/zink/zink_resource.h | 6 ++++++ 5 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/gallium/drivers/zink/zink_batch.c b/src/gallium/drivers/zink/zink_batch.c index 5df3887b449..28c60d8af00 100644 --- a/src/gallium/drivers/zink/zink_batch.c +++ b/src/gallium/drivers/zink/zink_batch.c @@ -804,8 +804,8 @@ zink_batch_usage_check_completion(struct zink_context *ctx, const struct zink_ba return zink_check_batch_completion(ctx, u->usage); } -void -zink_batch_usage_wait(struct zink_context *ctx, struct zink_batch_usage *u) +static void +batch_usage_wait(struct zink_context *ctx, struct zink_batch_usage *u, bool trywait) { if (!zink_batch_usage_exists(u)) return; @@ -814,9 +814,25 @@ zink_batch_usage_wait(struct zink_context *ctx, struct zink_batch_usage *u) ctx->base.flush(&ctx->base, NULL, PIPE_FLUSH_HINT_FINISH); else { //multi-context mtx_lock(&u->mtx); - cnd_wait(&u->flush, &u->mtx); + if (trywait) { + struct timespec ts = {0, 10000}; + cnd_timedwait(&u->flush, &u->mtx, &ts); + } else + cnd_wait(&u->flush, &u->mtx); mtx_unlock(&u->mtx); } } zink_wait_on_batch(ctx, u->usage); } + +void +zink_batch_usage_wait(struct zink_context *ctx, struct zink_batch_usage *u) +{ + batch_usage_wait(ctx, u, false); +} + +void +zink_batch_usage_try_wait(struct zink_context *ctx, struct zink_batch_usage *u) +{ + batch_usage_wait(ctx, u, true); +} diff --git a/src/gallium/drivers/zink/zink_batch.h b/src/gallium/drivers/zink/zink_batch.h index f911a4fd9df..9c9caa17e3a 100644 --- a/src/gallium/drivers/zink/zink_batch.h +++ b/src/gallium/drivers/zink/zink_batch.h @@ -121,6 +121,9 @@ zink_batch_usage_check_completion(struct zink_context *ctx, const struct zink_ba void zink_batch_usage_wait(struct zink_context *ctx, struct zink_batch_usage *u); +void +zink_batch_usage_try_wait(struct zink_context *ctx, struct zink_batch_usage *u); + #ifdef __cplusplus } #endif diff --git a/src/gallium/drivers/zink/zink_bo.h b/src/gallium/drivers/zink/zink_bo.h index 4d3a89b2b42..b58c6c2576c 100644 --- a/src/gallium/drivers/zink/zink_bo.h +++ b/src/gallium/drivers/zink/zink_bo.h @@ -182,6 +182,15 @@ zink_bo_usage_wait(struct zink_context *ctx, struct zink_bo *bo, enum zink_resou zink_batch_usage_wait(ctx, bo->writes); } +static inline void +zink_bo_usage_try_wait(struct zink_context *ctx, struct zink_bo *bo, enum zink_resource_access access) +{ + if (access & ZINK_RESOURCE_ACCESS_READ) + zink_batch_usage_try_wait(ctx, bo->reads); + if (access & ZINK_RESOURCE_ACCESS_WRITE) + zink_batch_usage_try_wait(ctx, bo->writes); +} + static inline void zink_bo_usage_set(struct zink_bo *bo, struct zink_batch_state *bs, bool write) { diff --git a/src/gallium/drivers/zink/zink_resource.c b/src/gallium/drivers/zink/zink_resource.c index af55ab236a0..e3b793ee7f6 100644 --- a/src/gallium/drivers/zink/zink_resource.c +++ b/src/gallium/drivers/zink/zink_resource.c @@ -1859,6 +1859,7 @@ zink_buffer_map(struct pipe_context *pctx, !res->obj->host_visible)) { assert(!(usage & (TC_TRANSFER_MAP_THREADED_UNSYNC | PIPE_MAP_THREAD_SAFE))); if (!res->obj->host_visible || !(usage & PIPE_MAP_ONCE)) { +overwrite: trans->offset = box->x % screen->info.props.limits.minMemoryMapAlignment; trans->staging_res = pipe_buffer_create(&screen->base, PIPE_BIND_LINEAR, PIPE_USAGE_STAGING, box->width + trans->offset); if (!trans->staging_res) @@ -1880,9 +1881,14 @@ zink_buffer_map(struct pipe_context *pctx, } if (!(usage & PIPE_MAP_UNSYNCHRONIZED)) { - if (usage & PIPE_MAP_WRITE) + if (usage & PIPE_MAP_WRITE) { + if (!(usage & PIPE_MAP_READ)) { + zink_resource_usage_try_wait(ctx, res, ZINK_RESOURCE_ACCESS_RW); + if (zink_resource_has_unflushed_usage(res)) + goto overwrite; + } zink_resource_usage_wait(ctx, res, ZINK_RESOURCE_ACCESS_RW); - else + } else zink_resource_usage_wait(ctx, res, ZINK_RESOURCE_ACCESS_WRITE); res->obj->access = 0; res->obj->access_stage = 0; diff --git a/src/gallium/drivers/zink/zink_resource.h b/src/gallium/drivers/zink/zink_resource.h index 4f2eb8ff64d..652585cc7ab 100644 --- a/src/gallium/drivers/zink/zink_resource.h +++ b/src/gallium/drivers/zink/zink_resource.h @@ -131,6 +131,12 @@ zink_resource_usage_check_completion(struct zink_screen *screen, struct zink_res return zink_bo_usage_check_completion(screen, res->obj->bo, access); } +static inline void +zink_resource_usage_try_wait(struct zink_context *ctx, struct zink_resource *res, enum zink_resource_access access) +{ + zink_bo_usage_try_wait(ctx, res->obj->bo, access); +} + static inline void zink_resource_usage_wait(struct zink_context *ctx, struct zink_resource *res, enum zink_resource_access access) {