diff --git a/src/vulkan/runtime/meson.build b/src/vulkan/runtime/meson.build index 2c373009c4b..317b8e60975 100644 --- a/src/vulkan/runtime/meson.build +++ b/src/vulkan/runtime/meson.build @@ -70,6 +70,7 @@ vulkan_runtime_files = files( 'vk_log.h', 'vk_meta.c', 'vk_meta.h', + 'vk_meta_draw_rects.c', 'vk_nir.c', 'vk_nir.h', 'vk_nir_convert_ycbcr.c', diff --git a/src/vulkan/runtime/vk_meta.c b/src/vulkan/runtime/vk_meta.c index a4752ac3fb2..30e22ec3707 100644 --- a/src/vulkan/runtime/vk_meta.c +++ b/src/vulkan/runtime/vk_meta.c @@ -21,10 +21,11 @@ * IN THE SOFTWARE. */ -#include "vk_meta.h" +#include "vk_meta_private.h" #include "vk_command_buffer.h" #include "vk_device.h" +#include "vk_pipeline.h" #include "vk_util.h" #include "util/hash_table.h" @@ -110,6 +111,9 @@ vk_meta_device_init(struct vk_device *device, cache_key_equal); simple_mtx_init(&meta->cache_mtx, mtx_plain); + meta->cmd_draw_rects = vk_meta_draw_rects; + meta->cmd_draw_volume = vk_meta_draw_volume; + return VK_SUCCESS; } @@ -228,6 +232,66 @@ vk_meta_create_pipeline_layout(struct vk_device *device, return VK_SUCCESS; } +static VkResult +create_rect_list_pipeline(struct vk_device *device, + struct vk_meta_device *meta, + const VkGraphicsPipelineCreateInfo *info, + VkPipeline *pipeline_out) +{ + const struct vk_device_dispatch_table *disp = &device->dispatch_table; + VkDevice _device = vk_device_to_handle(device); + + VkGraphicsPipelineCreateInfo info_local = *info; + + STACK_ARRAY(VkPipelineShaderStageCreateInfo, stages, info->stageCount + 1); + for (uint32_t i = 0; i < info->stageCount; i++) { + assert(info->pStages[i].stage != VK_SHADER_STAGE_VERTEX_BIT); + stages[i + 1] = info->pStages[i]; + } + + VkPipelineShaderStageNirCreateInfoMESA vs_nir_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_NIR_CREATE_INFO_MESA, + .nir = vk_meta_draw_rects_vs_nir(meta), + }; + stages[0] = (VkPipelineShaderStageCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = &vs_nir_info, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .pName = "main", + }; + + info_local.stageCount = info->stageCount + 1; + info_local.pStages = stages; + info_local.pVertexInputState = &vk_meta_draw_rects_vi_state; + info_local.pViewportState = &vk_meta_draw_rects_vs_state; + + uint32_t dyn_count = info->pDynamicState != NULL ? + info->pDynamicState->dynamicStateCount : 0; + + STACK_ARRAY(VkDynamicState, dyn_state, dyn_count + 2); + for (uint32_t i = 0; i < dyn_count; i++) + dyn_state[i] = info->pDynamicState->pDynamicStates[i]; + + dyn_state[dyn_count + 0] = VK_DYNAMIC_STATE_VIEWPORT; + dyn_state[dyn_count + 1] = VK_DYNAMIC_STATE_SCISSOR; + + const VkPipelineDynamicStateCreateInfo dyn_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .dynamicStateCount = dyn_count + 2, + .pDynamicStates = dyn_state, + }; + + info_local.pDynamicState = &dyn_info; + + VkResult result = disp->CreateGraphicsPipelines(_device, VK_NULL_HANDLE, + 1, &info_local, NULL, + pipeline_out); + + STACK_ARRAY_FINISH(dyn_state); + + return result; +} + static const VkPipelineRasterizationStateCreateInfo default_rs_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .depthClampEnable = false, @@ -254,6 +318,7 @@ vk_meta_create_graphics_pipeline(struct vk_device *device, { const struct vk_device_dispatch_table *disp = &device->dispatch_table; VkDevice _device = vk_device_to_handle(device); + VkResult result; VkGraphicsPipelineCreateInfo info_local = *info; @@ -268,6 +333,10 @@ vk_meta_create_graphics_pipeline(struct vk_device *device, }; __vk_append_struct(&info_local, &r_info); + /* Assume rectangle pipelines */ + if (info_local.pInputAssemblyState == NULL) + info_local.pInputAssemblyState = &vk_meta_draw_rects_ia_state; + if (info_local.pRasterizationState == NULL) info_local.pRasterizationState = &default_rs_info; @@ -304,9 +373,16 @@ vk_meta_create_graphics_pipeline(struct vk_device *device, } VkPipeline pipeline; - VkResult result = disp->CreateGraphicsPipelines(_device, VK_NULL_HANDLE, - 1, &info_local, - NULL, &pipeline); + if (info_local.pInputAssemblyState->topology == + VK_PRIMITIVE_TOPOLOGY_META_RECT_LIST_MESA) { + result = create_rect_list_pipeline(device, meta, + &info_local, + &pipeline); + } else { + result = disp->CreateGraphicsPipelines(_device, VK_NULL_HANDLE, + 1, &info_local, + NULL, &pipeline); + } if (unlikely(result != VK_SUCCESS)) return result; diff --git a/src/vulkan/runtime/vk_meta.h b/src/vulkan/runtime/vk_meta.h index fe66563e137..21dc38bda34 100644 --- a/src/vulkan/runtime/vk_meta.h +++ b/src/vulkan/runtime/vk_meta.h @@ -37,9 +37,34 @@ struct hash_table; struct vk_command_buffer; struct vk_device; +struct vk_meta_rect { + uint32_t x0, y0, x1, y1; + float z; + uint32_t layer; +}; + +#define VK_PRIMITIVE_TOPOLOGY_META_RECT_LIST_MESA (VkPrimitiveTopology)11 + struct vk_meta_device { struct hash_table *cache; simple_mtx_t cache_mtx; + + uint32_t max_bind_map_buffer_size_B; + + VkResult (*cmd_bind_map_buffer)(struct vk_command_buffer *cmd, + struct vk_meta_device *meta, + VkBuffer buffer, + void **map_out); + + void (*cmd_draw_rects)(struct vk_command_buffer *cmd, + struct vk_meta_device *meta, + uint32_t rect_count, + const struct vk_meta_rect *rects); + + void (*cmd_draw_volume)(struct vk_command_buffer *cmd, + struct vk_meta_device *meta, + const struct vk_meta_rect *rect, + uint32_t layer_count); }; VkResult vk_meta_device_init(struct vk_device *device, @@ -156,6 +181,16 @@ VkResult vk_meta_create_image_view(struct vk_command_buffer *cmd, const VkImageViewCreateInfo *info, VkImageView *image_view_out); +void vk_meta_draw_rects(struct vk_command_buffer *cmd, + struct vk_meta_device *meta, + uint32_t rect_count, + const struct vk_meta_rect *rects); + +void vk_meta_draw_volume(struct vk_command_buffer *cmd, + struct vk_meta_device *meta, + const struct vk_meta_rect *rect, + uint32_t layer_count); + #ifdef __cplusplus } #endif diff --git a/src/vulkan/runtime/vk_meta_draw_rects.c b/src/vulkan/runtime/vk_meta_draw_rects.c new file mode 100644 index 00000000000..cba81e80533 --- /dev/null +++ b/src/vulkan/runtime/vk_meta_draw_rects.c @@ -0,0 +1,282 @@ +/* + * 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_command_pool.h" +#include "vk_device.h" + +#include "nir_builder.h" + +const VkPipelineVertexInputStateCreateInfo vk_meta_draw_rects_vi_state = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = &(const VkVertexInputBindingDescription) { + .binding = 0, + .stride = 4 * sizeof(uint32_t), + .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, + }, + .vertexAttributeDescriptionCount = 1, + .pVertexAttributeDescriptions = &(const VkVertexInputAttributeDescription) { + .location = 0, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_UINT, + .offset = 0, + }, +}; + +const VkPipelineInputAssemblyStateCreateInfo vk_meta_draw_rects_ia_state = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = VK_PRIMITIVE_TOPOLOGY_META_RECT_LIST_MESA, + .primitiveRestartEnable = VK_FALSE, +}; + +const VkPipelineViewportStateCreateInfo vk_meta_draw_rects_vs_state = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = 1, + .scissorCount = 1, +}; + +nir_shader * +vk_meta_draw_rects_vs_nir(struct vk_meta_device *device) +{ + nir_builder build = nir_builder_init_simple_shader(MESA_SHADER_VERTEX, NULL, + "vk-meta-draw-rects-vs"); + nir_builder *b = &build; + + nir_variable *in = nir_variable_create(b->shader, nir_var_shader_in, + glsl_uvec4_type(), "vtx_in"); + in->data.location = VERT_ATTRIB_GENERIC0; + + nir_variable *pos = nir_variable_create(b->shader, nir_var_shader_out, + glsl_vec4_type(), "gl_Position"); + pos->data.location = VARYING_SLOT_POS; + + nir_variable *layer = nir_variable_create(b->shader, nir_var_shader_out, + glsl_int_type(), "gl_Layer"); + layer->data.location = VARYING_SLOT_LAYER; + + nir_ssa_def *vtx = nir_load_var(b, in); + nir_store_var(b, pos, nir_vec4(b, nir_channel(b, vtx, 0), + nir_channel(b, vtx, 1), + nir_channel(b, vtx, 2), + nir_imm_float(b, 1)), + 0xf); + nir_store_var(b, layer, nir_iadd(b, nir_load_instance_id(b), + nir_channel(b, vtx, 3)), + 0x1); + + return b->shader; +} + +struct vertex { + float x, y, z; + uint32_t layer; +}; + +static void +setup_viewport_scissor(struct vk_command_buffer *cmd, + uint32_t rect_count, + const struct vk_meta_rect *rects, + float *x_scale, float *y_scale) +{ + const struct vk_device_dispatch_table *disp = + &cmd->base.device->dispatch_table; + VkCommandBuffer _cmd = vk_command_buffer_to_handle(cmd); + + assert(rects[0].x0 < rects[0].x1 && rects[0].y0 < rects[0].y1); + uint32_t xbits = rects[0].x1 - 1, ybits = rects[0].y1 - 1; + float zmin = rects[0].z, zmax = rects[0].z; + for (uint32_t r = 1; r < rect_count; r++) { + assert(rects[r].x0 < rects[r].x1 && rects[r].y0 < rects[r].y1); + xbits |= rects[r].x1 - 1; + ybits |= rects[r].y1 - 1; + zmin = fminf(zmin, rects[r].z); + zmax = fminf(zmax, rects[r].z); + } + + /* Annoyingly, we don't actually know the render area. We assume that all + * our rects are inside the render area. We further assume the maximum + * image and/or viewport size is a power of two. This means we can round + * up to a power of two without going outside any maximums. Using a power + * of two will ensure we don't lose precision when scaling coordinates. + */ + int xmax_log2 = 1 + util_logbase2(xbits); + int ymax_log2 = 1 + util_logbase2(ybits); + + assert(xmax_log2 >= 0 && xmax_log2 <= 31); + assert(ymax_log2 >= 0 && ymax_log2 <= 31); + + /* We don't care about precise bounds on Z, only that it's inside [0, 1] if + * the implementaiton only supports [0, 1]. + */ + if (zmin >= 0.0f && zmax <= 1.0f) { + zmin = 0.0f; + zmax = 1.0f; + } + + VkViewport viewport = { + .x = 0, + .y = 0, + .width = ldexpf(1.0, xmax_log2), + .height = ldexpf(1.0, ymax_log2), + .minDepth = zmin, + .maxDepth = zmax, + }; + disp->CmdSetViewport(_cmd, 0, 1, &viewport); + + VkRect2D scissor = { + .offset = { 0, 0 }, + .extent = { 1u << xmax_log2, 1u << ymax_log2 }, + }; + disp->CmdSetScissor(_cmd, 0, 1, &scissor); + + /* Scaling factors */ + *x_scale = ldexpf(2.0, -xmax_log2); + *y_scale = ldexpf(2.0, -ymax_log2); +} + +static const uint32_t rect_vb_size_B = 6 * 4 * sizeof(float); + +static VkResult +create_vertex_buffer(struct vk_command_buffer *cmd, + struct vk_meta_device *meta, + float x_scale, float y_scale, + uint32_t rect_count, + const struct vk_meta_rect *rects, + VkBuffer *buffer_out) +{ + VkResult result; + + const VkBufferCreateInfo vtx_buffer_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = rect_count * rect_vb_size_B, + .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + .queueFamilyIndexCount = 1, + .pQueueFamilyIndices = &cmd->pool->queue_family_index, + }; + + result = vk_meta_create_buffer(cmd, meta, &vtx_buffer_info, buffer_out); + if (unlikely(result != VK_SUCCESS)) + return result; + + void *map; + result = meta->cmd_bind_map_buffer(cmd, meta, *buffer_out, &map); + if (unlikely(result != VK_SUCCESS)) + return result; + + for (uint32_t r = 0; r < rect_count; r++) { + float x0 = rects[r].x0 * x_scale - 1.0f; + float y0 = rects[r].y0 * y_scale - 1.0f; + float x1 = rects[r].x1 * x_scale - 1.0f; + float y1 = rects[r].y1 * y_scale - 1.0f; + float z = rects[r].z; + uint32_t w = rects[r].layer; + + struct vertex rect_vb_data[6] = { + { x0, y1, z, w }, + { x0, y0, z, w }, + { x1, y1, z, w }, + + { x1, y0, z, w }, + { x1, y1, z, w }, + { x0, y0, z, w }, + }; + assert(sizeof(rect_vb_data) == rect_vb_size_B); + memcpy((char *)map + r * rect_vb_size_B, rect_vb_data, rect_vb_size_B); + } + + return VK_SUCCESS; +} + +void +vk_meta_draw_volume(struct vk_command_buffer *cmd, + struct vk_meta_device *meta, + const struct vk_meta_rect *rect, + uint32_t layer_count) +{ + const struct vk_device_dispatch_table *disp = + &cmd->base.device->dispatch_table; + VkCommandBuffer _cmd = vk_command_buffer_to_handle(cmd); + + float x_scale, y_scale; + setup_viewport_scissor(cmd, 1, rect, &x_scale, &y_scale); + + VkBuffer vtx_buffer; + VkResult result = create_vertex_buffer(cmd, meta, x_scale, y_scale, + 1, rect, &vtx_buffer); + if (unlikely(result != VK_SUCCESS)) { + /* TODO: Report error */ + return; + } + + const VkDeviceSize zero = 0; + disp->CmdBindVertexBuffers(_cmd, 0, 1, &vtx_buffer, &zero); + + disp->CmdDraw(_cmd, 6, layer_count, 0, 0); +} + +void +vk_meta_draw_rects(struct vk_command_buffer *cmd, + struct vk_meta_device *meta, + uint32_t rect_count, + const struct vk_meta_rect *rects) +{ + const struct vk_device_dispatch_table *disp = + &cmd->base.device->dispatch_table; + VkCommandBuffer _cmd = vk_command_buffer_to_handle(cmd); + + /* Two triangles with VK_FORMAT_R16G16_UINT */ + const uint32_t rect_vb_size_B = 6 * 3 * sizeof(float); + const uint32_t rects_per_draw = + meta->max_bind_map_buffer_size_B / rect_vb_size_B; + + if (rect_count == 0) + return; + + float x_scale, y_scale; + setup_viewport_scissor(cmd, rect_count, rects, &x_scale, &y_scale); + + uint32_t next_rect = 0; + while (next_rect < rect_count) { + const uint32_t count = MIN2(rects_per_draw, rect_count - next_rect); + + VkBuffer vtx_buffer; + VkResult result = create_vertex_buffer(cmd, meta, x_scale, y_scale, + count, &rects[next_rect], + &vtx_buffer); + if (unlikely(result != VK_SUCCESS)) { + /* TODO: Report error */ + return; + } + + const VkDeviceSize zero = 0; + disp->CmdBindVertexBuffers(_cmd, 0, 1, &vtx_buffer, &zero); + + disp->CmdDraw(_cmd, 6 * count, 1, 0, 0); + + next_rect += count; + } + assert(next_rect == rect_count); +} diff --git a/src/vulkan/runtime/vk_meta_private.h b/src/vulkan/runtime/vk_meta_private.h new file mode 100644 index 00000000000..39c0a0fb943 --- /dev/null +++ b/src/vulkan/runtime/vk_meta_private.h @@ -0,0 +1,42 @@ +/* + * 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. + */ +#ifndef VK_META_PRIVATE_H +#define VK_META_PRIVATE_H + +#include "vk_meta.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const VkPipelineVertexInputStateCreateInfo vk_meta_draw_rects_vi_state; +extern const VkPipelineInputAssemblyStateCreateInfo vk_meta_draw_rects_ia_state; +extern const VkPipelineViewportStateCreateInfo vk_meta_draw_rects_vs_state; + +struct nir_shader *vk_meta_draw_rects_vs_nir(struct vk_meta_device *device); + +#ifdef __cplusplus +} +#endif + +#endif /* VK_META_PRIVATE_H */