From daa4485ecf24673a5b751e29fc1f13ce369e2181 Mon Sep 17 00:00:00 2001 From: Faith Ekstrand Date: Mon, 30 Jan 2023 20:11:53 -0600 Subject: [PATCH] vulkan/meta: Implement attachment clears Part-of: --- src/vulkan/runtime/meson.build | 1 + src/vulkan/runtime/vk_meta.h | 8 + src/vulkan/runtime/vk_meta_clear.c | 337 +++++++++++++++++++++++++++ src/vulkan/runtime/vk_meta_private.h | 13 ++ 4 files changed, 359 insertions(+) create mode 100644 src/vulkan/runtime/vk_meta_clear.c diff --git a/src/vulkan/runtime/meson.build b/src/vulkan/runtime/meson.build index 317b8e60975..707114b266d 100644 --- a/src/vulkan/runtime/meson.build +++ b/src/vulkan/runtime/meson.build @@ -71,6 +71,7 @@ vulkan_runtime_files = files( 'vk_meta.c', 'vk_meta.h', 'vk_meta_draw_rects.c', + 'vk_meta_clear.c', 'vk_nir.c', 'vk_nir.h', 'vk_nir_convert_ycbcr.c', diff --git a/src/vulkan/runtime/vk_meta.h b/src/vulkan/runtime/vk_meta.h index 21dc38bda34..d0c36a5aad6 100644 --- a/src/vulkan/runtime/vk_meta.h +++ b/src/vulkan/runtime/vk_meta.h @@ -191,6 +191,14 @@ void vk_meta_draw_volume(struct vk_command_buffer *cmd, const struct vk_meta_rect *rect, uint32_t layer_count); +void vk_meta_clear_attachments(struct vk_command_buffer *cmd, + struct vk_meta_device *meta, + const struct vk_meta_rendering_info *render, + uint32_t attachment_count, + const VkClearAttachment *attachments, + uint32_t rect_count, + const VkClearRect *rects); + #ifdef __cplusplus } #endif diff --git a/src/vulkan/runtime/vk_meta_clear.c b/src/vulkan/runtime/vk_meta_clear.c new file mode 100644 index 00000000000..c9ad7790d42 --- /dev/null +++ b/src/vulkan/runtime/vk_meta_clear.c @@ -0,0 +1,337 @@ +/* + * Copyright © 2022 Collabora Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "vk_meta_private.h" + +#include "vk_command_buffer.h" +#include "vk_device.h" +#include "vk_format.h" +#include "vk_image.h" +#include "vk_pipeline.h" +#include "vk_util.h" + +#include "nir_builder.h" + +struct vk_meta_clear_key { + struct vk_meta_rendering_info render; + uint8_t color_attachments_cleared; + bool clear_depth; + bool clear_stencil; +}; + +struct vk_meta_clear_push_data { + VkClearColorValue color_values[MESA_VK_MAX_COLOR_ATTACHMENTS]; +}; + +static nir_shader * +build_clear_shader(const struct vk_meta_clear_key *key) +{ + nir_builder build = nir_builder_init_simple_shader(MESA_SHADER_FRAGMENT, + NULL, "vk-meta-clear"); + nir_builder *b = &build; + + struct glsl_struct_field push_field = { + .type = glsl_array_type(glsl_vec4_type(), + MESA_VK_MAX_COLOR_ATTACHMENTS, + 16 /* explicit_stride */), + .name = "color_values", + }; + const struct glsl_type *push_iface_type = + glsl_interface_type(&push_field, 1, GLSL_INTERFACE_PACKING_STD140, + false /* row_major */, "push"); + + nir_variable *push = nir_variable_create(b->shader, nir_var_mem_push_const, + push_iface_type, "push"); + nir_deref_instr *push_arr = + nir_build_deref_struct(b, nir_build_deref_var(b, push), 0); + + u_foreach_bit(a, key->color_attachments_cleared) { + nir_ssa_def *color_value = + nir_load_deref(b, nir_build_deref_array_imm(b, push_arr, a)); + + const struct glsl_type *out_type; + if (vk_format_is_int(key->render.color_attachment_formats[a])) + out_type = glsl_ivec4_type(); + else if (vk_format_is_int(key->render.color_attachment_formats[a])) + out_type = glsl_uvec4_type(); + else + out_type = glsl_vec4_type(); + + char out_name[8]; + snprintf(out_name, sizeof(out_name), "color%u", a); + + nir_variable *out = nir_variable_create(b->shader, nir_var_shader_out, + out_type, out_name); + out->data.location = FRAG_RESULT_DATA0 + a; + + nir_store_var(b, out, color_value, 0xf); + } + + return b->shader; +} + +static VkResult +get_clear_pipeline_layout(struct vk_device *device, + struct vk_meta_device *meta, + VkPipelineLayout *layout_out) +{ + const char key[] = "vk-meta-clear-pipeline-layout"; + + VkPipelineLayout from_cache = + vk_meta_lookup_pipeline_layout(meta, key, sizeof(key)); + if (from_cache != VK_NULL_HANDLE) { + *layout_out = from_cache; + return VK_SUCCESS; + } + + const VkPushConstantRange push_range = { + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .offset = 0, + .size = sizeof(struct vk_meta_clear_push_data), + }; + + const VkPipelineLayoutCreateInfo info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pushConstantRangeCount = 1, + .pPushConstantRanges = &push_range, + }; + + return vk_meta_create_pipeline_layout(device, meta, &info, + key, sizeof(key), layout_out); +} + +static VkResult +get_clear_pipeline(struct vk_device *device, + struct vk_meta_device *meta, + const struct vk_meta_clear_key *key, + VkPipelineLayout layout, + VkPipeline *pipeline_out) +{ + VkPipeline from_cache = vk_meta_lookup_pipeline(meta, key, sizeof(*key)); + if (from_cache != VK_NULL_HANDLE) { + *pipeline_out = from_cache; + return VK_SUCCESS; + } + + const VkPipelineShaderStageNirCreateInfoMESA fs_nir_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_NIR_CREATE_INFO_MESA, + .nir = build_clear_shader(key), + }; + const VkPipelineShaderStageCreateInfo fs_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = &fs_nir_info, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .pName = "main", + }; + + VkPipelineDepthStencilStateCreateInfo ds_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + }; + if (key->clear_depth) { + ds_info.depthTestEnable = VK_TRUE; + ds_info.depthWriteEnable = VK_TRUE; + ds_info.depthCompareOp = VK_COMPARE_OP_ALWAYS; + } + if (key->clear_stencil) { + ds_info.stencilTestEnable = VK_TRUE; + ds_info.front.compareOp = VK_COMPARE_OP_ALWAYS; + ds_info.front.passOp = VK_STENCIL_OP_REPLACE; + ds_info.front.compareMask = ~0u; + ds_info.front.writeMask = ~0u; + ds_info.back = ds_info.front; + } + + const VkGraphicsPipelineCreateInfo info = { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .stageCount = 1, + .pStages = &fs_info, + .pDepthStencilState = &ds_info, + .layout = layout, + }; + + VkResult result = vk_meta_create_graphics_pipeline(device, meta, &info, + &key->render, + key, sizeof(*key), + pipeline_out); + ralloc_free(fs_nir_info.nir); + + return result; +} + +static int +vk_meta_rect_cmp_layer(const void *_a, const void *_b) +{ + const struct vk_meta_rect *a = _a, *b = _b; + assert(a->layer <= INT_MAX && b->layer <= INT_MAX); + return a->layer - b->layer; +} + +void +vk_meta_clear_attachments(struct vk_command_buffer *cmd, + struct vk_meta_device *meta, + const struct vk_meta_rendering_info *render, + uint32_t attachment_count, + const VkClearAttachment *attachments, + uint32_t clear_rect_count, + const VkClearRect *clear_rects) +{ + struct vk_device *device = cmd->base.device; + const struct vk_device_dispatch_table *disp = &device->dispatch_table; + VkResult result; + + struct vk_meta_clear_key key; + memset(&key, 0, sizeof(key)); + vk_meta_rendering_info_copy(&key.render, render); + + struct vk_meta_clear_push_data push = { }; + float depth_value = 1.0f; + uint32_t stencil_value = 0; + + for (uint32_t i = 0; i < attachment_count; i++) { + if (attachments[i].aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + const uint32_t a = attachments[i].colorAttachment; + if (a == VK_ATTACHMENT_UNUSED) + continue; + + assert(a < MESA_VK_MAX_COLOR_ATTACHMENTS); + if (render->color_attachment_formats[a] == VK_FORMAT_UNDEFINED) + continue; + + key.color_attachments_cleared |= BITFIELD_BIT(a); + push.color_values[a] = attachments[i].clearValue.color; + } + if (attachments[i].aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + key.clear_depth = true; + depth_value = attachments[i].clearValue.depthStencil.depth; + } + if (attachments[i].aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + key.clear_stencil = true; + stencil_value = attachments[i].clearValue.depthStencil.stencil; + } + } + + VkPipelineLayout layout; + result = get_clear_pipeline_layout(device, meta, &layout); + if (unlikely(result != VK_SUCCESS)) { + /* TODO: Report error */ + return; + } + + VkPipeline pipeline; + result = get_clear_pipeline(device, meta, &key, layout, &pipeline); + if (unlikely(result != VK_SUCCESS)) { + /* TODO: Report error */ + return; + } + + disp->CmdBindPipeline(vk_command_buffer_to_handle(cmd), + VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + if (key.clear_stencil) { + disp->CmdSetStencilReference(vk_command_buffer_to_handle(cmd), + VK_STENCIL_FACE_FRONT_AND_BACK, + stencil_value); + } + + disp->CmdPushConstants(vk_command_buffer_to_handle(cmd), + layout, VK_SHADER_STAGE_FRAGMENT_BIT, + 0, sizeof(push), &push); + + if (render->view_mask == 0) { + if (clear_rect_count == 1 && clear_rects[0].layerCount > 1) { + struct vk_meta_rect rect = { + .x0 = clear_rects[0].rect.offset.x, + .x1 = clear_rects[0].rect.offset.x + + clear_rects[0].rect.extent.width, + .y0 = clear_rects[0].rect.offset.y, + .y1 = clear_rects[0].rect.offset.y + + clear_rects[0].rect.extent.height, + .z = depth_value, + .layer = clear_rects[0].baseArrayLayer, + }; + + meta->cmd_draw_volume(cmd, meta, &rect, clear_rects[0].layerCount); + } else { + uint32_t max_rect_count = 0; + for (uint32_t r = 0; r < clear_rect_count; r++) + max_rect_count += clear_rects[r].layerCount; + + STACK_ARRAY(struct vk_meta_rect, rects, max_rect_count); + + uint32_t rect_count = 0; + for (uint32_t r = 0; r < clear_rect_count; r++) { + struct vk_meta_rect rect = { + .x0 = clear_rects[r].rect.offset.x, + .x1 = clear_rects[r].rect.offset.x + + clear_rects[r].rect.extent.width, + .y0 = clear_rects[r].rect.offset.y, + .y1 = clear_rects[r].rect.offset.y + + clear_rects[r].rect.extent.height, + .z = depth_value, + }; + for (uint32_t a = 0; a < clear_rects[r].layerCount; a++) { + rect.layer = clear_rects[r].baseArrayLayer + a; + rects[rect_count++] = rect; + } + } + assert(rect_count <= max_rect_count); + + /* If we have more than one clear rect, sort by layer in the hopes + * the hardware more or less does all the clears for one layer before + * moving on to the next, thus reducing cache thrashing. + */ + qsort(rects, rect_count, sizeof(*rects), vk_meta_rect_cmp_layer); + + meta->cmd_draw_rects(cmd, meta, rect_count, rects); + + STACK_ARRAY_FINISH(rects); + } + } else { + const uint32_t rect_count = clear_rect_count * + util_bitcount(render->view_mask); + STACK_ARRAY(struct vk_meta_rect, rects, rect_count); + + uint32_t rect_idx = 0; + u_foreach_bit(v, render->view_mask) { + for (uint32_t r = 0; r < clear_rect_count; r++) { + assert(clear_rects[r].baseArrayLayer == 0); + assert(clear_rects[r].layerCount == 1); + rects[rect_idx++] = (struct vk_meta_rect) { + .x0 = clear_rects[r].rect.offset.x, + .x1 = clear_rects[r].rect.offset.x + + clear_rects[r].rect.extent.width, + .y0 = clear_rects[r].rect.offset.y, + .y1 = clear_rects[r].rect.offset.y + + clear_rects[r].rect.extent.height, + .z = depth_value, + .layer = v, + }; + } + } + assert(rect_idx == rect_count); + + meta->cmd_draw_rects(cmd, meta, rect_count, rects); + + STACK_ARRAY_FINISH(rects); + } +} diff --git a/src/vulkan/runtime/vk_meta_private.h b/src/vulkan/runtime/vk_meta_private.h index 39c0a0fb943..40deac94405 100644 --- a/src/vulkan/runtime/vk_meta_private.h +++ b/src/vulkan/runtime/vk_meta_private.h @@ -35,6 +35,19 @@ extern const VkPipelineViewportStateCreateInfo vk_meta_draw_rects_vs_state; struct nir_shader *vk_meta_draw_rects_vs_nir(struct vk_meta_device *device); +static inline void +vk_meta_rendering_info_copy(struct vk_meta_rendering_info *dst, + const struct vk_meta_rendering_info *src) +{ + dst->view_mask = src->view_mask; + dst->samples = src->samples; + dst->color_attachment_count = src->color_attachment_count; + for (uint32_t a = 0; a < src->color_attachment_count; a++) + dst->color_attachment_formats[a] = src->color_attachment_formats[a]; + dst->depth_attachment_format = src->depth_attachment_format; + dst->stencil_attachment_format = src->stencil_attachment_format; +} + #ifdef __cplusplus } #endif