Workaround an issue with some R600 ASICs that causes deadlock (GPU lockup) situations during heavy 3D usage. It is thought, that it may be caused by a HW bug on R6xx where you need to re-emit a CB register if some state further up the pipeline changes even if the CB state has not changed. The issue is reproducible with OpenArena, Urban Terror, Warsow, and World of Padman after some minutes of game play. (Applied on Mesa git commit 07cdfdb708ac28aa487a738db30b128cb0df1dc3 .) Should fix: Mesa Bug 50655 & 58058 https://bugs.freedesktop.org/show_bug.cgi?id=50655 https://bugs.freedesktop.org/show_bug.cgi?id=58058 Signed-off-by: Erik Jørgensen --- src/gallium/drivers/r600/r600.h | 1 + src/gallium/drivers/r600/r600_hw_context.c | 39 +++++++++++++++++++++++--- src/gallium/drivers/r600/r600_state_common.c | 6 ++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/gallium/drivers/r600/r600.h b/src/gallium/drivers/r600/r600.h index 08b77e4..3fef68e 100644 --- a/src/gallium/drivers/r600/r600.h +++ b/src/gallium/drivers/r600/r600.h @@ -151,6 +151,7 @@ struct r600_so_target { #define R600_CONTEXT_WAIT_CP_DMA_IDLE (1 << 3) #define R600_CONTEXT_FLUSH_AND_INV (1 << 4) #define R600_CONTEXT_FLUSH_AND_INV_CB_META (1 << 5) +#define R600_CONTEXT_CB0_EMITFLUSH (1 << 6) struct r600_context; struct r600_screen; diff --git a/src/gallium/drivers/r600/r600_hw_context.c b/src/gallium/drivers/r600/r600_hw_context.c index ebcd682..53c7fe3 100644 --- a/src/gallium/drivers/r600/r600_hw_context.c +++ b/src/gallium/drivers/r600/r600_hw_context.c @@ -694,6 +694,36 @@ void r600_flush_emit(struct r600_context *rctx) emit_flush = 1; } + /* Workaround for HW deadlock on R600 class of GPU ASICs. */ + if (rctx->family >= CHIP_R600 && rctx->family <= CHIP_RS880) { + if (rctx->flags & R600_CONTEXT_STREAMOUT_FLUSH) { + /* RV670 errata. */ + if (rctx->family == CHIP_RV670 || + rctx->family == CHIP_RS780 || + rctx->family == CHIP_RS880) { + cp_coher_cntl |= S_0085F0_DEST_BASE_0_ENA(1); + emit_flush = 1; + } + } + + if (rctx->flags & R600_CONTEXT_CB0_EMITFLUSH) { + /* Emit a CB. Some ASICs may also need CB1 in conjuction with that to work out right. */ + cp_coher_cntl |= S_0085F0_CB_ACTION_ENA(1) | + S_0085F0_CB0_DEST_BASE_ENA(1) | + S_0085F0_CB1_DEST_BASE_ENA(1); + + /* RV670 errata + * (CB1_DEST_BASE_ENA is also required, which is + * included unconditionally above). */ + if (rctx->family == CHIP_RV670 || + rctx->family == CHIP_RS780 || + rctx->family == CHIP_RS880) { + cp_coher_cntl |= S_0085F0_DEST_BASE_0_ENA(1); + } + emit_flush = 1; + } + } + if (emit_flush) { cs->buf[cs->cdw++] = PKT3(PKT3_SURFACE_SYNC, 3, 0); cs->buf[cs->cdw++] = cp_coher_cntl; /* CP_COHER_CNTL */ @@ -1102,12 +1132,12 @@ void r600_cp_dma_copy_buffer(struct r600_context *rctx, struct radeon_winsys_cs *cs = rctx->rings.gfx.cs; assert(size); - assert(rctx->chip_class != R600); + /* assert(rctx->chip_class != R600); */ - /* CP DMA doesn't work on R600 (flushing seems to be unreliable). */ + /* CP DMA doesn't work on R600 (flushing seems to be unreliable). if (rctx->chip_class == R600) { return; - } + } */ dst_offset += r600_resource_va(&rctx->screen->screen, dst); src_offset += r600_resource_va(&rctx->screen->screen, src); @@ -1118,7 +1148,8 @@ void r600_cp_dma_copy_buffer(struct r600_context *rctx, R600_CONTEXT_FLUSH_AND_INV | R600_CONTEXT_FLUSH_AND_INV_CB_META | R600_CONTEXT_STREAMOUT_FLUSH | - R600_CONTEXT_WAIT_3D_IDLE; + R600_CONTEXT_WAIT_3D_IDLE | + R600_CONTEXT_CB0_EMITFLUSH; /* There are differences between R700 and EG in CP DMA, * but we only use the common bits here. */ diff --git a/src/gallium/drivers/r600/r600_state_common.c b/src/gallium/drivers/r600/r600_state_common.c index c03ce3d..dec697c 100644 --- a/src/gallium/drivers/r600/r600_state_common.c +++ b/src/gallium/drivers/r600/r600_state_common.c @@ -1359,6 +1359,12 @@ static void r600_draw_vbo(struct pipe_context *ctx, const struct pipe_draw_info rctx->vgt_state.atom.dirty = true; } + /* Workaround for hardware deadlock on certain R600 ASICs: write into a CB register. */ + if (rctx->chip_class == R600) { + rctx->flags |= R600_CONTEXT_CB0_EMITFLUSH; + rctx->cb_misc_state.atom.dirty = true; + } + /* Emit states. */ r600_need_cs_space(rctx, ib.user_buffer ? 5 : 0, TRUE); r600_flush_emit(rctx);