diff options
author | Iago Toral Quiroga <itoral@igalia.com> | 2020-03-31 12:59:44 +0200 |
---|---|---|
committer | Marge Bot <eric+marge@anholt.net> | 2020-10-13 21:21:29 +0000 |
commit | 7a39e5e9022958b68af55d06ae0e48ae20a93b17 (patch) | |
tree | a65cfa18bdc4acdebbcf208a7f0598e880ccd82c | |
parent | 152a64185b2a3e9b92478e22a164851e661c1c08 (diff) |
v3dv: implement partial color attachment clears
This is achieved by rendering a quad in the clear color for each layer
of each attachment being cleared. Right now we emit each clear in a
separate job with a single attachment framebuffer, but in the future
we may be able to extend the solution to using multiple render targets
and clear multiple attachments with a single job.
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6766>
-rw-r--r-- | src/broadcom/vulkan/v3dv_cmd_buffer.c | 78 | ||||
-rw-r--r-- | src/broadcom/vulkan/v3dv_device.c | 2 | ||||
-rw-r--r-- | src/broadcom/vulkan/v3dv_meta_clear.c | 494 | ||||
-rw-r--r-- | src/broadcom/vulkan/v3dv_pipeline.c | 6 | ||||
-rw-r--r-- | src/broadcom/vulkan/v3dv_private.h | 31 |
5 files changed, 587 insertions, 24 deletions
diff --git a/src/broadcom/vulkan/v3dv_cmd_buffer.c b/src/broadcom/vulkan/v3dv_cmd_buffer.c index c32b5c4c8c2..d6175936b59 100644 --- a/src/broadcom/vulkan/v3dv_cmd_buffer.c +++ b/src/broadcom/vulkan/v3dv_cmd_buffer.c @@ -72,12 +72,6 @@ v3dv_job_add_extra_bo(struct v3dv_job *job, struct v3dv_bo *bo) _mesa_set_add(job->extra_bos, bo); } -static struct v3dv_job * -subpass_start(struct v3dv_cmd_buffer *cmd_buffer, uint32_t subpass_idx); - -static void -subpass_finish(struct v3dv_cmd_buffer *cmd_buffer); - static void cmd_buffer_emit_render_pass_rcl(struct v3dv_cmd_buffer *cmd_buffer); @@ -133,6 +127,9 @@ cmd_buffer_init(struct v3dv_cmd_buffer *cmd_buffer, assert(pool); list_addtail(&cmd_buffer->pool_link, &pool->cmd_buffers); + cmd_buffer->state.subpass_idx = -1; + cmd_buffer->state.meta.subpass_idx = -1; + cmd_buffer->status = V3DV_CMD_BUFFER_STATUS_INITIALIZED; } @@ -887,7 +884,7 @@ v3dv_CmdBeginRenderPass(VkCommandBuffer commandBuffer, state->render_area = pRenderPassBegin->renderArea; /* Setup for first subpass */ - subpass_start(cmd_buffer, 0); + v3dv_cmd_buffer_subpass_start(cmd_buffer, 0); } void @@ -899,10 +896,10 @@ v3dv_CmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents) assert(state->subpass_idx < state->pass->subpass_count - 1); /* Finish the previous subpass */ - subpass_finish(cmd_buffer); + v3dv_cmd_buffer_subpass_finish(cmd_buffer); /* Start the next subpass */ - subpass_start(cmd_buffer, state->subpass_idx + 1); + v3dv_cmd_buffer_subpass_start(cmd_buffer, state->subpass_idx + 1); } void @@ -1517,8 +1514,9 @@ cmd_buffer_emit_render_pass_rcl(struct v3dv_cmd_buffer *cmd_buffer) cl_emit(rcl, END_OF_RENDERING, end); } -static struct v3dv_job * -subpass_start(struct v3dv_cmd_buffer *cmd_buffer, uint32_t subpass_idx) +struct v3dv_job * +v3dv_cmd_buffer_subpass_start(struct v3dv_cmd_buffer *cmd_buffer, + uint32_t subpass_idx) { struct v3dv_cmd_buffer_state *state = &cmd_buffer->state; assert(subpass_idx < state->pass->subpass_count); @@ -1568,8 +1566,8 @@ subpass_start(struct v3dv_cmd_buffer *cmd_buffer, uint32_t subpass_idx) return job; } -static void -subpass_finish(struct v3dv_cmd_buffer *cmd_buffer) +void +v3dv_cmd_buffer_subpass_finish(struct v3dv_cmd_buffer *cmd_buffer) { struct v3dv_job *job = cmd_buffer->state.job; assert(job); @@ -1584,7 +1582,7 @@ v3dv_CmdEndRenderPass(VkCommandBuffer commandBuffer) /* Emit last subpass */ struct v3dv_cmd_buffer_state *state = &cmd_buffer->state; assert(state->subpass_idx == state->pass->subpass_count - 1); - subpass_finish(cmd_buffer); + v3dv_cmd_buffer_subpass_finish(cmd_buffer); v3dv_cmd_buffer_finish_job(cmd_buffer); /* We are no longer inside a render pass */ @@ -2507,6 +2505,55 @@ emit_gl_shader_state(struct v3dv_cmd_buffer *cmd_buffer) V3DV_CMD_DIRTY_PUSH_CONSTANTS); } +/* This stores command buffer state for the current subpass that we are + * about to interrupt to emit a meta pass. + */ +void +v3dv_cmd_buffer_meta_state_push(struct v3dv_cmd_buffer *cmd_buffer) +{ + struct v3dv_cmd_buffer_state *state = &cmd_buffer->state; + assert(state->subpass_idx != -1); + + state->meta.pipeline = v3dv_pipeline_to_handle(state->pipeline); + state->meta.framebuffer = v3dv_framebuffer_to_handle(state->framebuffer); + state->meta.pass = v3dv_render_pass_to_handle(state->pass); + state->meta.subpass_idx = state->subpass_idx; +} + +/* This resstores command buffer state for a subpass that we interrupted to + * emit a meta pass. + */ +void +v3dv_cmd_buffer_meta_state_pop(struct v3dv_cmd_buffer *cmd_buffer) +{ + struct v3dv_cmd_buffer_state *state = &cmd_buffer->state; + assert(state->meta.subpass_idx != -1); + + state->pass = v3dv_render_pass_from_handle(state->meta.pass); + state->framebuffer = v3dv_framebuffer_from_handle(state->meta.framebuffer); + + struct v3dv_job *job = + v3dv_cmd_buffer_subpass_start(cmd_buffer, state->meta.subpass_idx); + if (job) { + job->is_subpass_continue = true; + if (state->meta.pipeline != VK_NULL_HANDLE) { + v3dv_CmdBindPipeline(v3dv_cmd_buffer_to_handle(cmd_buffer), + VK_PIPELINE_BIND_POINT_GRAPHICS, + state->meta.pipeline); + } else { + state->pipeline = VK_NULL_HANDLE; + } + } else { + state->pipeline = VK_NULL_HANDLE; + state->subpass_idx = -1; + } + + state->meta.pipeline = VK_NULL_HANDLE; + state->meta.framebuffer = VK_NULL_HANDLE; + state->meta.pass = VK_NULL_HANDLE; + state->meta.subpass_idx = -1; +} + /* FIXME: C&P from v3dx_draw. Refactor to common place? */ static uint32_t v3d_hw_prim_type(enum pipe_prim_type prim_type) @@ -2583,7 +2630,8 @@ cmd_buffer_pre_draw_split_job(struct v3dv_cmd_buffer *cmd_buffer) /* 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); + job = v3dv_cmd_buffer_subpass_start(cmd_buffer, + cmd_buffer->state.subpass_idx); assert(job->draw_count == 0); job->is_subpass_continue = true; diff --git a/src/broadcom/vulkan/v3dv_device.c b/src/broadcom/vulkan/v3dv_device.c index 0374f70ee1b..d7814b0829f 100644 --- a/src/broadcom/vulkan/v3dv_device.c +++ b/src/broadcom/vulkan/v3dv_device.c @@ -1111,7 +1111,7 @@ v3dv_CreateDevice(VkPhysicalDevice physicalDevice, return vk_error(instance, VK_ERROR_INITIALIZATION_FAILED); } - device = vk_alloc2(&physical_device->instance->alloc, pAllocator, + device = vk_zalloc2(&physical_device->instance->alloc, pAllocator, sizeof(*device), 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE); if (!device) diff --git a/src/broadcom/vulkan/v3dv_meta_clear.c b/src/broadcom/vulkan/v3dv_meta_clear.c index 03c88678ed1..3aba40461e3 100644 --- a/src/broadcom/vulkan/v3dv_meta_clear.c +++ b/src/broadcom/vulkan/v3dv_meta_clear.c @@ -24,21 +24,503 @@ #include "v3dv_private.h" #include "broadcom/cle/v3dx_pack.h" +#include "compiler/nir/nir_builder.h" #include "vk_format_info.h" +static nir_ssa_def * +gen_rect_vertices(nir_builder *b) +{ + nir_intrinsic_instr *vertex_id = + nir_intrinsic_instr_create(b->shader, + nir_intrinsic_load_vertex_id); + nir_ssa_dest_init(&vertex_id->instr, &vertex_id->dest, 1, 32, "vertexid"); + nir_builder_instr_insert(b, &vertex_id->instr); + + + /* vertex 0: -1.0, -1.0 + * vertex 1: -1.0, 1.0 + * vertex 2: 1.0, -1.0 + * vertex 3: 1.0, 1.0 + * + * so: + * + * channel 0 is vertex_id < 2 ? -1.0 : 1.0 + * channel 1 is vertex id & 1 ? 1.0 : -1.0 + */ + + nir_ssa_def *one = nir_imm_int(b, 1); + nir_ssa_def *c0cmp = nir_ilt(b, &vertex_id->dest.ssa, nir_imm_int(b, 2)); + nir_ssa_def *c1cmp = nir_ieq(b, nir_iand(b, &vertex_id->dest.ssa, one), one); + + nir_ssa_def *comp[4]; + comp[0] = nir_bcsel(b, c0cmp, + nir_imm_float(b, -1.0f), + nir_imm_float(b, 1.0f)); + + comp[1] = nir_bcsel(b, c1cmp, + nir_imm_float(b, 1.0f), + nir_imm_float(b, -1.0f)); + comp[2] = nir_imm_float(b, 0.0f); + comp[3] = nir_imm_float(b, 1.0f); + return nir_vec(b, comp, 4); +} + +static nir_shader * +get_color_clear_rect_vs() +{ + nir_builder b; + const nir_shader_compiler_options *options = v3dv_pipeline_get_nir_options(); + nir_builder_init_simple_shader(&b, NULL, MESA_SHADER_VERTEX, options); + b.shader->info.name = ralloc_strdup(b.shader, "meta clear vs"); + + const struct glsl_type *vec4 = glsl_vec4_type(); + nir_variable *vs_out_pos = + nir_variable_create(b.shader, nir_var_shader_out, vec4, "gl_Position"); + vs_out_pos->data.location = VARYING_SLOT_POS; + + nir_ssa_def *pos = gen_rect_vertices(&b); + nir_store_var(&b, vs_out_pos, pos, 0xf); + + return b.shader; +} + +static nir_shader * +get_color_clear_rect_fs(struct v3dv_render_pass *pass, uint32_t rt_idx) +{ + nir_builder b; + const nir_shader_compiler_options *options = v3dv_pipeline_get_nir_options(); + nir_builder_init_simple_shader(&b, NULL, MESA_SHADER_FRAGMENT, options); + b.shader->info.name = ralloc_strdup(b.shader, "meta clear fs"); + + /* Since our implementation can only clear one RT at a time we know there + * is a single subpass with a single attachment. + */ + assert(pass->attachment_count == 1); + enum pipe_format pformat = + vk_format_to_pipe_format(pass->attachments[0].desc.format); + const struct glsl_type *fs_out_type = + util_format_is_float(pformat) ? glsl_vec4_type() : glsl_uvec4_type(); + + nir_variable *fs_out_color = + nir_variable_create(b.shader, nir_var_shader_out, fs_out_type, "out_color"); + fs_out_color->data.location = FRAG_RESULT_DATA0 + rt_idx; + + nir_intrinsic_instr *color_load = + nir_intrinsic_instr_create(b.shader, nir_intrinsic_load_push_constant); + nir_intrinsic_set_base(color_load, 0); + nir_intrinsic_set_range(color_load, 16); + color_load->src[0] = nir_src_for_ssa(nir_imm_int(&b, 0)); + color_load->num_components = 4; + nir_ssa_dest_init(&color_load->instr, &color_load->dest, 4, 32, "clear color"); + nir_builder_instr_insert(&b, &color_load->instr); + + nir_store_var(&b, fs_out_color, &color_load->dest.ssa, 0xf); + + return b.shader; +} + +static VkResult +create_color_clear_pipeline_layout(struct v3dv_device *device, + VkPipelineLayout *pipeline_layout) +{ + VkPipelineLayoutCreateInfo info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = 0, + .pushConstantRangeCount = 1, + .pPushConstantRanges = + &(VkPushConstantRange) { VK_SHADER_STAGE_FRAGMENT_BIT, 0, 16 }, + }; + + return v3dv_CreatePipelineLayout(v3dv_device_to_handle(device), + &info, &device->alloc, pipeline_layout); +} + +static VkResult +create_pipeline(struct v3dv_device *device, + struct v3dv_render_pass *pass, + uint32_t samples, + struct nir_shader *vs_nir, + struct nir_shader *fs_nir, + const VkPipelineVertexInputStateCreateInfo *vi_state, + const VkPipelineDepthStencilStateCreateInfo *ds_state, + const VkPipelineColorBlendStateCreateInfo *cb_state, + const VkPipelineLayout layout, + VkPipeline *pipeline) +{ + struct v3dv_shader_module vs_m = { .nir = vs_nir }; + struct v3dv_shader_module fs_m = { .nir = fs_nir }; + + VkPipelineShaderStageCreateInfo stages[2] = { + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = v3dv_shader_module_to_handle(&vs_m), + .pName = "main", + }, + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .module = v3dv_shader_module_to_handle(&fs_m), + .pName = "main", + }, + }; + + VkGraphicsPipelineCreateInfo info = { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + + .stageCount = 2, + .pStages = stages, + + .pVertexInputState = vi_state, + + .pInputAssemblyState = &(VkPipelineInputAssemblyStateCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + .primitiveRestartEnable = false, + }, + + .pViewportState = &(VkPipelineViewportStateCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = 1, + .scissorCount = 1, + }, + + .pRasterizationState = &(VkPipelineRasterizationStateCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .rasterizerDiscardEnable = false, + .polygonMode = VK_POLYGON_MODE_FILL, + .cullMode = VK_CULL_MODE_NONE, + .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, + .depthBiasEnable = false, + }, + + .pMultisampleState = &(VkPipelineMultisampleStateCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .rasterizationSamples = samples, + .sampleShadingEnable = false, + .pSampleMask = NULL, + .alphaToCoverageEnable = false, + .alphaToOneEnable = false, + }, + + .pDepthStencilState = ds_state, + + .pColorBlendState = cb_state, + + /* The meta clear pipeline declares all state as dynamic. + * As a consequence, vkCmdBindPipeline writes no dynamic state + * to the cmd buffer. Therefore, at the end of the meta clear, + * we need only restore dynamic state that was vkCmdSet. + * + * FIXME: Update this when we support more dynamic states (adding + * them now will assert because they are not supported). + */ + .pDynamicState = &(VkPipelineDynamicStateCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .dynamicStateCount = 6, + .pDynamicStates = (VkDynamicState[]) { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, + VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, + VK_DYNAMIC_STATE_STENCIL_REFERENCE, + VK_DYNAMIC_STATE_BLEND_CONSTANTS, +#if 0 + VK_DYNAMIC_STATE_LINE_WIDTH, + VK_DYNAMIC_STATE_DEPTH_BIAS, + VK_DYNAMIC_STATE_DEPTH_BOUNDS, +#endif + }, + }, + + .flags = 0, + .layout = layout, + .renderPass = v3dv_render_pass_to_handle(pass), + .subpass = 0, + }; + + VkResult result = + v3dv_CreateGraphicsPipelines(v3dv_device_to_handle(device), + VK_NULL_HANDLE, + 1, &info, + &device->alloc, + pipeline); + + ralloc_free(vs_nir); + ralloc_free(fs_nir); + + return result; +} + +static VkResult +create_color_clear_pipeline(struct v3dv_device *device, + uint32_t rt_idx, + uint32_t samples, + VkRenderPass _pass, + VkPipelineLayout *pipeline_layout, + VkPipeline *pipeline) +{ + /* For now we only support clearing a framebuffer with a single attachment */ + assert(rt_idx == 0); + + struct v3dv_render_pass *pass = v3dv_render_pass_from_handle(_pass); + + nir_shader *vs_nir = get_color_clear_rect_vs(); + nir_shader *fs_nir = get_color_clear_rect_fs(pass, rt_idx); + + const VkPipelineVertexInputStateCreateInfo vi_state = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .vertexBindingDescriptionCount = 0, + .vertexAttributeDescriptionCount = 0, + }; + + const VkPipelineDepthStencilStateCreateInfo ds_state = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + .depthTestEnable = false, + .depthWriteEnable = false, + .depthBoundsTestEnable = false, + .stencilTestEnable = false, + }; + + /* FIXME: for now our color clear pipeline can only clear a single RT, + * but in the future we might want to be able to support multiple render + * targets. If we do that, then we might also be able to implement partial + * color clearing for vkCmdClearAttachments without having to split the + * subpass job at all. + */ + VkPipelineColorBlendAttachmentState blend_att_state[1] = { 0 }; + blend_att_state[rt_idx] = (VkPipelineColorBlendAttachmentState) { + .blendEnable = false, + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | + VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | + VK_COLOR_COMPONENT_A_BIT, + }; + + const VkPipelineColorBlendStateCreateInfo cb_state = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .logicOpEnable = false, + .attachmentCount = 1, + .pAttachments = blend_att_state + }; + + VkResult result = create_color_clear_pipeline_layout(device, pipeline_layout); + if (result != VK_SUCCESS) + return result; + + return create_pipeline(device, + pass, + samples, + vs_nir, fs_nir, + &vi_state, + &ds_state, + &cb_state, + *pipeline_layout, + pipeline); +} + +static VkResult +create_color_clear_render_pass(struct v3dv_device *device, + VkFormat format, + uint32_t samples, + VkRenderPass *pass) +{ + VkAttachmentDescription att = { + .format = format, + .samples = samples, + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .initialLayout = VK_IMAGE_LAYOUT_GENERAL, + .finalLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + + VkAttachmentReference att_ref = { + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_GENERAL, + }; + + VkSubpassDescription subpass = { + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .inputAttachmentCount = 0, + .colorAttachmentCount = 1, + .pColorAttachments = &att_ref, + .pResolveAttachments = NULL, + .pDepthStencilAttachment = NULL, + .preserveAttachmentCount = 0, + .pPreserveAttachments = NULL, + }; + + VkRenderPassCreateInfo info = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .attachmentCount = 1, + .pAttachments = &att, + .subpassCount = 1, + .pSubpasses = &subpass, + .dependencyCount = 0, + .pDependencies = NULL, + }; + + return v3dv_CreateRenderPass(v3dv_device_to_handle(device), + &info, &device->alloc, pass); +} + +static VkResult +ensure_color_clear_pipeline(struct v3dv_device *device, + VkFormat format, + uint32_t samples) +{ + /* FIXME: we need a pipeline per [format, samples], right now this + * only works for the first combination we need. + */ + VkResult result = VK_SUCCESS; + if (device->meta.color_clear.pipeline) + return result; + + if (!device->meta.color_clear.pass) { + result = + create_color_clear_render_pass(device, + format, + samples, + &device->meta.color_clear.pass); + if (result != VK_SUCCESS) + return result; + } + + if (!device->meta.color_clear.pipeline) { + result = + create_color_clear_pipeline(device, 0 /* rt_idx*/, samples, + device->meta.color_clear.pass, + &device->meta.color_clear.playout, + &device->meta.color_clear.pipeline); + if (result != VK_SUCCESS) + return result; + } + + return result; +} + static void emit_color_clear_rect(struct v3dv_cmd_buffer *cmd_buffer, - uint32_t rt, + uint32_t rt_idx, VkClearColorValue clear_color, - VkClearRect rect) + const VkClearRect *rect) { - assert(!"Not implemented."); + assert(cmd_buffer->state.pass); + struct v3dv_device *device = cmd_buffer->device; + struct v3dv_render_pass *pass = cmd_buffer->state.pass; + struct v3dv_subpass *subpass = + &pass->subpasses[cmd_buffer->state.subpass_idx]; + + assert(rt_idx < subpass->color_count); + uint32_t attachment_idx = subpass->color_attachments[rt_idx].attachment; + assert(attachment_idx != VK_ATTACHMENT_UNUSED && + attachment_idx < pass->attachment_count); + VkFormat rt_format = pass->attachments[attachment_idx].desc.format; + const uint32_t rt_samples = pass->attachments[attachment_idx].desc.samples; + + if (ensure_color_clear_pipeline(device, rt_format, rt_samples) != VK_SUCCESS) + return; + + /* Store command buffer state for the current subpass before we interrupt + * it to emit the color clear pass and then finish the job for the + * interrupted subpass. + */ + v3dv_cmd_buffer_meta_state_push(cmd_buffer); + v3dv_cmd_buffer_finish_job(cmd_buffer); + + struct v3dv_framebuffer *subpass_fb = + v3dv_framebuffer_from_handle(cmd_buffer->state.meta.framebuffer); + VkCommandBuffer cmd_buffer_handle = v3dv_cmd_buffer_to_handle(cmd_buffer); + VkDevice device_handle = v3dv_device_to_handle(cmd_buffer->device); + + /* Emit the pass for each attachment layer, which creates a framebuffer + * for each selected layer of the attachment and then renders a scissored + * quad in the clear color. + */ + for (uint32_t i = 0; i < rect->layerCount; i++) { + struct v3dv_image_view attachment_layer_view; + memcpy(&attachment_layer_view, + subpass_fb->attachments[rt_idx], + sizeof(struct v3dv_image_view)); + attachment_layer_view.first_layer += rect->baseArrayLayer + i; + attachment_layer_view.last_layer = attachment_layer_view.first_layer; + VkImageView fb_attachment = + v3dv_image_view_to_handle(&attachment_layer_view); + + VkFramebufferCreateInfo fb_info = { + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .renderPass = v3dv_render_pass_to_handle(pass), + .attachmentCount = 1, + .pAttachments = &fb_attachment, + .width = subpass_fb->width, + .height = subpass_fb->height, + .layers = 1, + }; + + VkFramebuffer fb; + v3dv_CreateFramebuffer(device_handle, &fb_info, + &cmd_buffer->device->alloc, &fb); + + VkRenderPassBeginInfo rp_info = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .renderPass = device->meta.color_clear.pass, + .framebuffer = fb, + .renderArea = { .offset = { 0, 0 }, + .extent = { fb_info.width, fb_info.height } }, + .clearValueCount = 0, + }; + + v3dv_CmdBeginRenderPass(cmd_buffer_handle, &rp_info, + VK_SUBPASS_CONTENTS_INLINE); + + struct v3dv_job *job = cmd_buffer->state.job; + if (!job) { + v3dv_DestroyFramebuffer(device_handle, fb, NULL); + goto fail_job_start; + } + job->is_subpass_continue = true; + + v3dv_CmdPushConstants(cmd_buffer_handle, + device->meta.color_clear.playout, + VK_SHADER_STAGE_FRAGMENT_BIT, 0, 16, + &clear_color); + + v3dv_CmdBindPipeline(cmd_buffer_handle, + VK_PIPELINE_BIND_POINT_GRAPHICS, + device->meta.color_clear.pipeline); + + const VkViewport viewport = { + .x = rect->rect.offset.x, + .y = rect->rect.offset.y, + .width = rect->rect.extent.width, + .height = rect->rect.extent.height, + .minDepth = 0.0f, + .maxDepth = 1.0f + }; + v3dv_CmdSetViewport(cmd_buffer_handle, 0, 1, &viewport); + v3dv_CmdSetScissor(cmd_buffer_handle, 0, 1, &rect->rect); + + v3dv_CmdDraw(cmd_buffer_handle, 4, 1, 0, 0); + + v3dv_CmdEndRenderPass(cmd_buffer_handle); + + /* The Vulkan spec doesn't allow to destroy the framebuffer until all + * command buffers that use it have completed execution, however, in + * our particular case this is fine, since copy into all framebuffer + * info we need to submit and execute the job into the command buffer, + * so we don't need to keep the framebuffer object around. + */ + v3dv_DestroyFramebuffer(device_handle, fb, &cmd_buffer->device->alloc); + } + +fail_job_start: + v3dv_cmd_buffer_meta_state_pop(cmd_buffer); } static void emit_ds_clear_rect(struct v3dv_cmd_buffer *cmd_buffer, VkClearDepthStencilValue clear_ds, - VkClearRect rect) + const VkClearRect *rect) { assert(!"Not implemented."); } @@ -531,13 +1013,13 @@ v3dv_CmdClearAttachments(VkCommandBuffer commandBuffer, emit_color_clear_rect(cmd_buffer, rt_idx, pAttachments[i].clearValue.color, - pRects[j]); + &pRects[j]); } } else { for (uint32_t j = 0; j < rectCount; j++) { emit_ds_clear_rect(cmd_buffer, pAttachments[i].clearValue.depthStencil, - pRects[j]); + &pRects[j]); } } } diff --git a/src/broadcom/vulkan/v3dv_pipeline.c b/src/broadcom/vulkan/v3dv_pipeline.c index c0ce412340e..7ff1d47fb62 100644 --- a/src/broadcom/vulkan/v3dv_pipeline.c +++ b/src/broadcom/vulkan/v3dv_pipeline.c @@ -192,6 +192,12 @@ const nir_shader_compiler_options v3dv_nir_options = { * needs to be supported */ }; +const nir_shader_compiler_options * +v3dv_pipeline_get_nir_options(void) +{ + return &v3dv_nir_options; +} + #define OPT(pass, ...) ({ \ bool this_progress = false; \ NIR_PASS(this_progress, nir, pass, ##__VA_ARGS__); \ diff --git a/src/broadcom/vulkan/v3dv_private.h b/src/broadcom/vulkan/v3dv_private.h index ed6a99ce35d..c72bb6fc4f2 100644 --- a/src/broadcom/vulkan/v3dv_private.h +++ b/src/broadcom/vulkan/v3dv_private.h @@ -236,6 +236,15 @@ struct v3dv_device { * the GPU is idle. */ uint32_t last_job_sync; + + /* Resources used for meta operations */ + struct { + struct { + VkPipelineLayout playout; + VkPipeline pipeline; + VkRenderPass pass; + } color_clear; + } meta; }; struct v3dv_device_memory { @@ -660,8 +669,8 @@ struct v3dv_descriptor_state { }; struct v3dv_cmd_buffer_state { - const struct v3dv_render_pass *pass; - const struct v3dv_framebuffer *framebuffer; + struct v3dv_render_pass *pass; + struct v3dv_framebuffer *framebuffer; VkRect2D render_area; /* Current job being recorded */ @@ -684,6 +693,14 @@ struct v3dv_cmd_buffer_state { /* Used to flag OOM conditions during command buffer recording */ bool oom; + + /* Command buffer state saved during a meta operation */ + struct { + uint32_t subpass_idx; + VkRenderPass pass; + VkPipeline pipeline; + VkFramebuffer framebuffer; + } meta; }; struct v3dv_descriptor { @@ -738,6 +755,14 @@ struct v3dv_job *v3dv_cmd_buffer_start_job(struct v3dv_cmd_buffer *cmd_buffer, int32_t subpass_idx); void v3dv_cmd_buffer_finish_job(struct v3dv_cmd_buffer *cmd_buffer); +struct v3dv_job *v3dv_cmd_buffer_subpass_start(struct v3dv_cmd_buffer *cmd_buffer, + uint32_t subpass_idx); + +void v3dv_cmd_buffer_subpass_finish(struct v3dv_cmd_buffer *cmd_buffer); + +void v3dv_cmd_buffer_meta_state_push(struct v3dv_cmd_buffer *cmd_buffer); +void v3dv_cmd_buffer_meta_state_pop(struct v3dv_cmd_buffer *cmd_buffer); + void v3dv_render_pass_setup_render_target(struct v3dv_cmd_buffer *cmd_buffer, int rt, uint32_t *rt_bpp, @@ -1055,6 +1080,8 @@ struct v3dv_pipeline { uint8_t stencil_cfg[2][cl_packet_length(STENCIL_CFG)]; }; +const nir_shader_compiler_options *v3dv_pipeline_get_nir_options(void); + static inline uint32_t v3dv_zs_buffer_from_aspect_bits(VkImageAspectFlags aspects) { |