From 761b0c02f8625e1eb0e045242fcf59bf988c1956 Mon Sep 17 00:00:00 2001 From: Iago Toral Quiroga Date: Wed, 18 Mar 2020 10:00:31 +0100 Subject: [PATCH] v3dv: add an 'always flush' mode In this mode, which can be activated with V3D_DEBUG=always_flush like in the GL driver, we flush every draw call separately. For now this is useful for debugging, but we can also set the flag internally on specific jobs when we identify scenarios where we need the same behavior. Part-of: --- src/broadcom/vulkan/v3dv_cmd_buffer.c | 67 +++++++++++++++++++++++---- src/broadcom/vulkan/v3dv_private.h | 9 ++++ 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/broadcom/vulkan/v3dv_cmd_buffer.c b/src/broadcom/vulkan/v3dv_cmd_buffer.c index 0eff6ae8b2a..e21eb331cb7 100644 --- a/src/broadcom/vulkan/v3dv_cmd_buffer.c +++ b/src/broadcom/vulkan/v3dv_cmd_buffer.c @@ -71,7 +71,7 @@ v3dv_job_add_extra_bo(struct v3dv_job *job, struct v3dv_bo *bo) _mesa_set_add(job->extra_bos, bo); } -static void +static struct v3dv_job * subpass_start(struct v3dv_cmd_buffer *cmd_buffer, uint32_t subpass_idx); static void @@ -257,6 +257,12 @@ cmd_buffer_can_merge_subpass(struct v3dv_cmd_buffer *cmd_buffer) const struct v3dv_physical_device *physical_device = &cmd_buffer->device->instance->physicalDevice; + if (!cmd_buffer->state.job) + return false; + + if (cmd_buffer->state.job->always_flush) + return false; + if (!physical_device->options.merge_jobs) return false; @@ -515,6 +521,9 @@ v3dv_job_init(struct v3dv_job *job, */ if (cmd_buffer && cmd_buffer->state.pass) job->first_subpass = subpass_idx; + + if (V3D_DEBUG & V3D_DEBUG_ALWAYS_FLUSH) + job->always_flush = true; } struct v3dv_job * @@ -1491,7 +1500,7 @@ cmd_buffer_emit_render_pass_rcl(struct v3dv_cmd_buffer *cmd_buffer) cl_emit(rcl, END_OF_RENDERING, end); } -static void +static struct v3dv_job * subpass_start(struct v3dv_cmd_buffer *cmd_buffer, uint32_t subpass_idx) { struct v3dv_cmd_buffer_state *state = &cmd_buffer->state; @@ -1503,7 +1512,7 @@ subpass_start(struct v3dv_cmd_buffer *cmd_buffer, uint32_t subpass_idx) */ struct v3dv_job *job = v3dv_cmd_buffer_start_job(cmd_buffer, subpass_idx); if (!job) - return; + return NULL; state->subpass_idx = subpass_idx; @@ -1538,6 +1547,8 @@ subpass_start(struct v3dv_cmd_buffer *cmd_buffer, uint32_t subpass_idx) cmd_buffer->state.dynamic.viewport.count == 0) { emit_clip_window(job, &state->render_area); } + + return job; } static void @@ -2331,9 +2342,46 @@ cmd_buffer_emit_draw(struct v3dv_cmd_buffer *cmd_buffer, } } +static struct v3dv_job * +cmd_buffer_pre_draw_split_job(struct v3dv_cmd_buffer *cmd_buffer) +{ + struct v3dv_job *job = cmd_buffer->state.job; + assert(job); + + /* If the job has been flagged with 'always_flush' and it has already + * recorded any draw calls then we need to start a new job for it. + */ + if (job->always_flush && job->draw_count > 0) { + assert(cmd_buffer->state.pass); + /* First, flag the current job as not being the last in the + * current subpass + */ + job->is_subpass_finish = false; + + /* Now start a new job in the same subpass and flag it as continuing + * the current subpass. + */ + job = subpass_start(cmd_buffer, cmd_buffer->state.subpass_idx); + assert(job->draw_count == 0); + job->is_subpass_continue = true; + + /* Inherit the 'always flush' behavior */ + job->always_flush = true; + } + + assert(job->draw_count == 0 || !job->always_flush); + return job; +} + static void cmd_buffer_emit_pre_draw(struct v3dv_cmd_buffer *cmd_buffer) { + /* If the job is configured to flush on every draw call we need to create + * a new job now. + */ + struct v3dv_job *job = cmd_buffer_pre_draw_split_job(cmd_buffer); + job->draw_count++; + /* FIXME: likely to be filtered by really needed states */ uint32_t *dirty = &cmd_buffer->state.dirty; struct v3dv_dynamic_state *dynamic = &cmd_buffer->state.dynamic; @@ -2383,7 +2431,6 @@ v3dv_CmdDraw(VkCommandBuffer commandBuffer, { V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, commandBuffer); struct v3dv_draw_info info = {}; - info.vertex_count = vertexCount; info.instance_count = instanceCount; info.first_instance = firstInstance; @@ -2402,11 +2449,11 @@ v3dv_CmdDrawIndexed(VkCommandBuffer commandBuffer, { V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, commandBuffer); + cmd_buffer_emit_pre_draw(cmd_buffer); + struct v3dv_job *job = cmd_buffer->state.job; assert(job); - cmd_buffer_emit_pre_draw(cmd_buffer); - const struct v3dv_pipeline *pipeline = cmd_buffer->state.pipeline; uint32_t hw_prim_type = v3d_hw_prim_type(pipeline->vs->topology); uint8_t index_type = ffs(cmd_buffer->state.index_size) - 1; @@ -2453,11 +2500,11 @@ v3dv_CmdDrawIndirect(VkCommandBuffer commandBuffer, if (drawCount == 0) return; + cmd_buffer_emit_pre_draw(cmd_buffer); + struct v3dv_job *job = cmd_buffer->state.job; assert(job); - cmd_buffer_emit_pre_draw(cmd_buffer); - const struct v3dv_pipeline *pipeline = cmd_buffer->state.pipeline; uint32_t hw_prim_type = v3d_hw_prim_type(pipeline->vs->topology); @@ -2483,11 +2530,11 @@ v3dv_CmdDrawIndexedIndirect(VkCommandBuffer commandBuffer, if (drawCount == 0) return; + cmd_buffer_emit_pre_draw(cmd_buffer); + struct v3dv_job *job = cmd_buffer->state.job; assert(job); - cmd_buffer_emit_pre_draw(cmd_buffer); - const struct v3dv_pipeline *pipeline = cmd_buffer->state.pipeline; uint32_t hw_prim_type = v3d_hw_prim_type(pipeline->vs->topology); uint8_t index_type = ffs(cmd_buffer->state.index_size) - 1; diff --git a/src/broadcom/vulkan/v3dv_private.h b/src/broadcom/vulkan/v3dv_private.h index 29d5014c69b..823d81cff9d 100644 --- a/src/broadcom/vulkan/v3dv_private.h +++ b/src/broadcom/vulkan/v3dv_private.h @@ -592,6 +592,15 @@ struct v3dv_job { * submit no-op jobs), then it is our responsibility to do that. */ struct v3dv_fence *fence; + + /* Number of draw calls recorded into the job */ + uint32_t draw_count; + + /* A flag indicating whether we want to flush every draw separately. This + * can be used for debugging, or for cases where special circumstances + * require this behavior. + */ + bool always_flush; }; void v3dv_job_init(struct v3dv_job *job,