diff --git a/src/amd/vulkan/radv_cmd_buffer.c b/src/amd/vulkan/radv_cmd_buffer.c index 223cab3d86c..5a7ea937c19 100644 --- a/src/amd/vulkan/radv_cmd_buffer.c +++ b/src/amd/vulkan/radv_cmd_buffer.c @@ -2236,6 +2236,71 @@ radv_load_color_clear_metadata(struct radv_cmd_buffer *cmd_buffer, } } +/* GFX9+ metadata cache flushing workaround. metadata cache coherency is + * broken if the CB caches data of multiple mips of the same image at the + * same time. + * + * Insert some flushes to avoid this. + */ +static void +radv_emit_fb_mip_change_flush(struct radv_cmd_buffer *cmd_buffer) +{ + struct radv_framebuffer *framebuffer = cmd_buffer->state.framebuffer; + const struct radv_subpass *subpass = cmd_buffer->state.subpass; + bool color_mip_changed = false; + + /* Entire workaround is not applicable before GFX9 */ + if (cmd_buffer->device->physical_device->rad_info.chip_class < GFX9) + return; + + if (!framebuffer) + return; + + for (int i = 0; i < subpass->color_count; ++i) { + int idx = subpass->color_attachments[i].attachment; + struct radv_image_view *iview = cmd_buffer->state.attachments[idx].iview; + + if ((radv_image_has_CB_metadata(iview->image) || + radv_image_has_dcc(iview->image)) && + cmd_buffer->state.cb_mip[i] != iview->base_mip) + color_mip_changed = true; + + cmd_buffer->state.cb_mip[i] = iview->base_mip; + } + + if (color_mip_changed) { + cmd_buffer->state.flush_bits |= RADV_CMD_FLAG_FLUSH_AND_INV_CB | + RADV_CMD_FLAG_FLUSH_AND_INV_CB_META; + } +} + +/* This function does the flushes for mip changes if the levels are not zero for + * all render targets. This way we can assume at the start of the next cmd_buffer + * that rendering to mip 0 doesn't need any flushes. As that is the most common + * case that saves some flushes. */ +static void +radv_emit_mip_change_flush_default(struct radv_cmd_buffer *cmd_buffer) +{ + /* Entire workaround is not applicable before GFX9 */ + if (cmd_buffer->device->physical_device->rad_info.chip_class < GFX9) + return; + + bool need_color_mip_flush = false; + for (unsigned i = 0; i < 8; ++i) { + if (cmd_buffer->state.cb_mip[i]) { + need_color_mip_flush = true; + break; + } + } + + if (need_color_mip_flush) { + cmd_buffer->state.flush_bits |= RADV_CMD_FLAG_FLUSH_AND_INV_CB | + RADV_CMD_FLAG_FLUSH_AND_INV_CB_META; + } + + memset(cmd_buffer->state.cb_mip, 0, sizeof(cmd_buffer->state.cb_mip)); +} + static void radv_emit_framebuffer_state(struct radv_cmd_buffer *cmd_buffer) { @@ -4075,6 +4140,8 @@ VkResult radv_EndCommandBuffer( { RADV_FROM_HANDLE(radv_cmd_buffer, cmd_buffer, commandBuffer); + radv_emit_mip_change_flush_default(cmd_buffer); + if (cmd_buffer->queue_family_index != RADV_QUEUE_TRANSFER) { if (cmd_buffer->device->physical_device->rad_info.chip_class == GFX6) cmd_buffer->state.flush_bits |= RADV_CMD_FLAG_CS_PARTIAL_FLUSH | RADV_CMD_FLAG_PS_PARTIAL_FLUSH | RADV_CMD_FLAG_WB_L2; @@ -4655,6 +4722,8 @@ void radv_CmdExecuteCommands( assert(commandBufferCount > 0); + radv_emit_mip_change_flush_default(primary); + /* Emit pending flushes on primary prior to executing secondary */ si_emit_cache_flush(primary); @@ -4687,6 +4756,7 @@ void radv_CmdExecuteCommands( * has been recorded without a framebuffer, otherwise * fast color/depth clears can't work. */ + radv_emit_fb_mip_change_flush(primary); radv_emit_framebuffer_state(primary); } @@ -5294,6 +5364,10 @@ radv_draw(struct radv_cmd_buffer *cmd_buffer, return; } + /* Need to apply this workaround early as it can set flush flags. */ + if (cmd_buffer->state.dirty & RADV_CMD_DIRTY_FRAMEBUFFER) + radv_emit_fb_mip_change_flush(cmd_buffer); + /* Use optimal packet order based on whether we need to sync the * pipeline. */ diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h index e9f16deb6da..ab7d797fbf5 100644 --- a/src/amd/vulkan/radv_private.h +++ b/src/amd/vulkan/radv_private.h @@ -1391,6 +1391,8 @@ struct radv_cmd_state { uint32_t num_layout_transitions; bool pending_sqtt_barrier_end; enum rgp_flush_bits sqtt_flush_bits; + + uint8_t cb_mip[MAX_RTS]; }; struct radv_cmd_pool {