// Copyright 2019-2020, Collabora, Ltd. // SPDX-License-Identifier: BSL-1.0 /*! * @file * @brief The NEW compositor rendering code header. * @author Lubosz Sarnecki * @author Jakob Bornecrantz * @ingroup comp_main */ #include "main/comp_compositor.h" #include "render/comp_render.h" #include /* * * Common helpers * */ #define C(c) \ do { \ VkResult ret = c; \ if (ret != VK_SUCCESS) { \ return false; \ } \ } while (false) #define D(TYPE, thing) \ if (thing != VK_NULL_HANDLE) { \ vk->vkDestroy##TYPE(vk->device, thing, NULL); \ thing = VK_NULL_HANDLE; \ } #define DD(pool, thing) \ if (thing != VK_NULL_HANDLE) { \ free_descriptor_set(vk, pool, thing); \ thing = VK_NULL_HANDLE; \ } /*! * Get the @ref vk_bundle from @ref comp_rendering_target_resources. */ static inline struct vk_bundle * vk_from_rts(struct comp_rendering_target_resources *rts) { return &rts->c->vk; } /*! * Get the @ref vk_bundle from @ref comp_rendering. */ static inline struct vk_bundle * vk_from_rr(struct comp_rendering *rr) { return &rr->c->vk; } static VkResult create_external_render_pass(struct vk_bundle *vk, VkFormat format, VkRenderPass *out_render_pass) { VkResult ret; VkAttachmentDescription attachments[1] = { { .format = format, .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, }, }; VkAttachmentReference color_reference = { .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }; VkSubpassDescription subpasses[1] = { { .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .inputAttachmentCount = 0, .pInputAttachments = NULL, .colorAttachmentCount = 1, .pColorAttachments = &color_reference, .pResolveAttachments = NULL, .pDepthStencilAttachment = NULL, .preserveAttachmentCount = 0, .pPreserveAttachments = NULL, }, }; VkSubpassDependency dependencies[1] = { { .srcSubpass = VK_SUBPASS_EXTERNAL, .dstSubpass = 0, .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, .srcAccessMask = 0, .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, }, }; VkRenderPassCreateInfo render_pass_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .attachmentCount = ARRAY_SIZE(attachments), .pAttachments = attachments, .subpassCount = ARRAY_SIZE(subpasses), .pSubpasses = subpasses, .dependencyCount = ARRAY_SIZE(dependencies), .pDependencies = dependencies, }; VkRenderPass render_pass = VK_NULL_HANDLE; ret = vk->vkCreateRenderPass(vk->device, // &render_pass_info, // NULL, // &render_pass); // if (ret != VK_SUCCESS) { VK_ERROR(vk, "vkCreateRenderPass failed: %s", vk_result_string(ret)); return ret; } *out_render_pass = render_pass; return VK_SUCCESS; } static VkResult create_descriptor_set(struct vk_bundle *vk, VkDescriptorPool descriptor_pool, VkDescriptorSetLayout descriptor_layout, VkDescriptorSet *out_descriptor_set) { VkResult ret; VkDescriptorSetAllocateInfo alloc_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .descriptorPool = descriptor_pool, .descriptorSetCount = 1, .pSetLayouts = &descriptor_layout, }; VkDescriptorSet descriptor_set = VK_NULL_HANDLE; ret = vk->vkAllocateDescriptorSets(vk->device, // &alloc_info, // &descriptor_set); // if (ret != VK_SUCCESS) { VK_DEBUG(vk, "vkAllocateDescriptorSets failed: %s", vk_result_string(ret)); return ret; } *out_descriptor_set = descriptor_set; return VK_SUCCESS; } static VkResult create_framebuffer(struct vk_bundle *vk, VkImageView image_view, VkRenderPass render_pass, uint32_t width, uint32_t height, VkFramebuffer *out_external_framebuffer) { VkResult ret; VkImageView attachments[1] = {image_view}; VkFramebufferCreateInfo frame_buffer_info = { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .renderPass = render_pass, .attachmentCount = ARRAY_SIZE(attachments), .pAttachments = attachments, .width = width, .height = height, .layers = 1, }; VkFramebuffer framebuffer = VK_NULL_HANDLE; ret = vk->vkCreateFramebuffer(vk->device, // &frame_buffer_info, // NULL, // &framebuffer); // if (ret != VK_SUCCESS) { VK_ERROR(vk, "vkCreateFramebuffer failed: %s", vk_result_string(ret)); return ret; } *out_external_framebuffer = framebuffer; return VK_SUCCESS; } static VkResult create_command_buffer(struct vk_bundle *vk, VkCommandBuffer *out_cmd) { VkResult ret; VkCommandBufferAllocateInfo cmd_buffer_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = vk->cmd_pool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; VkCommandBuffer cmd = VK_NULL_HANDLE; os_mutex_lock(&vk->cmd_pool_mutex); ret = vk->vkAllocateCommandBuffers(vk->device, // &cmd_buffer_info, // &cmd); // os_mutex_unlock(&vk->cmd_pool_mutex); if (ret != VK_SUCCESS) { VK_ERROR(vk, "vkCreateFramebuffer failed: %s", vk_result_string(ret)); return ret; } *out_cmd = cmd; return VK_SUCCESS; } static VkResult begin_command_buffer(struct vk_bundle *vk, VkCommandBuffer command_buffer) { VkResult ret; VkCommandBufferBeginInfo command_buffer_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; ret = vk->vkBeginCommandBuffer(command_buffer, &command_buffer_info); if (ret != VK_SUCCESS) { VK_ERROR(vk, "vkBeginCommandBuffer failed: %s", vk_result_string(ret)); return ret; } return VK_SUCCESS; } static void begin_render_pass(struct vk_bundle *vk, VkCommandBuffer command_buffer, VkRenderPass render_pass, VkFramebuffer framebuffer, uint32_t width, uint32_t height) { VkClearValue clear_color[1] = {{ .color = {.float32 = {0.0f, 0.0f, 0.0f, 0.0f}}, }}; VkRenderPassBeginInfo render_pass_begin_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderPass = render_pass, .framebuffer = framebuffer, .renderArea = { .offset = { .x = 0, .y = 0, }, .extent = { .width = width, .height = height, }, }, .clearValueCount = ARRAY_SIZE(clear_color), .pClearValues = clear_color, }; vk->vkCmdBeginRenderPass(command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); } /* * * Mesh * */ static VkResult create_mesh_pipeline(struct vk_bundle *vk, VkRenderPass render_pass, VkPipelineLayout pipeline_layout, VkPipelineCache pipeline_cache, uint32_t src_binding, uint32_t mesh_total_num_indices, uint32_t mesh_stride, VkShaderModule mesh_vert, VkShaderModule mesh_frag, VkPipeline *out_mesh_pipeline) { VkResult ret; // Might be changed to line for debugging. VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL; // Do we use triangle strips or triangles with indices. VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; if (mesh_total_num_indices > 0) { topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; } VkPipelineInputAssemblyStateCreateInfo input_assembly_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = topology, .primitiveRestartEnable = VK_FALSE, }; VkPipelineRasterizationStateCreateInfo rasterization_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .depthClampEnable = VK_FALSE, .rasterizerDiscardEnable = VK_FALSE, .polygonMode = polygonMode, .cullMode = VK_CULL_MODE_BACK_BIT, .frontFace = VK_FRONT_FACE_CLOCKWISE, .lineWidth = 1.0f, }; VkPipelineColorBlendAttachmentState blend_attachment_state = { .blendEnable = VK_FALSE, .colorWriteMask = 0xf, }; VkPipelineColorBlendStateCreateInfo color_blend_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .attachmentCount = 1, .pAttachments = &blend_attachment_state, }; VkPipelineDepthStencilStateCreateInfo depth_stencil_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .depthTestEnable = VK_TRUE, .depthWriteEnable = VK_TRUE, .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL, .front = { .compareOp = VK_COMPARE_OP_ALWAYS, }, .back = { .compareOp = VK_COMPARE_OP_ALWAYS, }, }; VkPipelineViewportStateCreateInfo viewport_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .viewportCount = 1, .scissorCount = 1, }; VkPipelineMultisampleStateCreateInfo multisample_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, }; VkDynamicState dynamic_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, }; VkPipelineDynamicStateCreateInfo dynamic_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .dynamicStateCount = ARRAY_SIZE(dynamic_states), .pDynamicStates = dynamic_states, }; // clang-format off VkVertexInputAttributeDescription vertex_input_attribute_descriptions[2] = { { .binding = src_binding, .location = 0, .format = VK_FORMAT_R32G32B32A32_SFLOAT, .offset = 0, }, { .binding = src_binding, .location = 1, .format = VK_FORMAT_R32G32B32A32_SFLOAT, .offset = 16, }, }; VkVertexInputBindingDescription vertex_input_binding_description[1] = { { .binding = src_binding, .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, .stride = mesh_stride, }, }; VkPipelineVertexInputStateCreateInfo vertex_input_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .vertexAttributeDescriptionCount = ARRAY_SIZE(vertex_input_attribute_descriptions), .pVertexAttributeDescriptions = vertex_input_attribute_descriptions, .vertexBindingDescriptionCount = ARRAY_SIZE(vertex_input_binding_description), .pVertexBindingDescriptions = vertex_input_binding_description, }; // clang-format on VkPipelineShaderStageCreateInfo shader_stages[2] = { { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = mesh_vert, .pName = "main", }, { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = mesh_frag, .pName = "main", }, }; VkGraphicsPipelineCreateInfo pipeline_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .stageCount = ARRAY_SIZE(shader_stages), .pStages = shader_stages, .pVertexInputState = &vertex_input_state, .pInputAssemblyState = &input_assembly_state, .pViewportState = &viewport_state, .pRasterizationState = &rasterization_state, .pMultisampleState = &multisample_state, .pDepthStencilState = &depth_stencil_state, .pColorBlendState = &color_blend_state, .pDynamicState = &dynamic_state, .layout = pipeline_layout, .renderPass = render_pass, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1, }; VkPipeline pipeline = VK_NULL_HANDLE; ret = vk->vkCreateGraphicsPipelines(vk->device, // pipeline_cache, // 1, // &pipeline_info, // NULL, // &pipeline); // if (ret != VK_SUCCESS) { VK_DEBUG(vk, "vkCreateGraphicsPipelines failed: %s", vk_result_string(ret)); return ret; } *out_mesh_pipeline = pipeline; return VK_SUCCESS; } static void update_mesh_discriptor_set(struct vk_bundle *vk, uint32_t src_binding, VkSampler sampler, VkImageView image_view, uint32_t ubo_binding, VkBuffer buffer, VkDeviceSize size, VkDescriptorSet descriptor_set) { VkDescriptorImageInfo image_info = { .sampler = sampler, .imageView = image_view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; VkDescriptorBufferInfo buffer_info = { .buffer = buffer, .offset = 0, .range = size, }; VkWriteDescriptorSet write_descriptor_sets[2] = { { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = descriptor_set, .dstBinding = src_binding, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .pImageInfo = &image_info, }, { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = descriptor_set, .dstBinding = ubo_binding, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .pBufferInfo = &buffer_info, }, }; vk->vkUpdateDescriptorSets(vk->device, // ARRAY_SIZE(write_descriptor_sets), // descriptorWriteCount write_descriptor_sets, // pDescriptorWrites 0, // descriptorCopyCount NULL); // pDescriptorCopies } /* * * 'Exported' target resourecs functions. * */ bool comp_rendering_target_resources_init(struct comp_rendering_target_resources *rts, struct comp_compositor *c, struct comp_resources *r, VkImageView target, struct comp_target_data *data) { struct vk_bundle *vk = &c->vk; rts->c = c; rts->r = r; assert(data->is_external); rts->data = *data; C(create_external_render_pass( // vk, // vk_bundle data->format, // target_format &rts->render_pass)); // out_render_pass C(create_mesh_pipeline(vk, // vk_bundle rts->render_pass, // render_pass r->mesh.pipeline_layout, // pipeline_layout r->pipeline_cache, // pipeline_cache r->mesh.src_binding, // src_binding r->mesh.total_num_indices, // mesh_total_num_indices r->mesh.stride, // mesh_stride rts->c->shaders.mesh_vert, // mesh_vert rts->c->shaders.mesh_frag, // mesh_frag &rts->mesh.pipeline)); // out_mesh_pipeline C(create_framebuffer(vk, // vk_bundle, target, // image_view, rts->render_pass, // render_pass, data->width, // width, data->height, // height, &rts->framebuffer)); // out_external_framebuffer return true; } void comp_rendering_target_resources_close(struct comp_rendering_target_resources *rts) { struct vk_bundle *vk = vk_from_rts(rts); D(RenderPass, rts->render_pass); D(Pipeline, rts->mesh.pipeline); D(Framebuffer, rts->framebuffer); } /* * * 'Exported' rendering functions. * */ bool comp_rendering_init(struct comp_rendering *rr, struct comp_compositor *c, struct comp_resources *r) { struct vk_bundle *vk = &c->vk; rr->c = c; rr->r = r; /* * Per rendering. */ C(create_command_buffer(vk, &rr->cmd)); C(begin_command_buffer(vk, rr->cmd)); /* * Mesh per view */ C(create_descriptor_set( // vk, // vk_bundle r->mesh.descriptor_pool, // descriptor_pool r->mesh.descriptor_set_layout, // descriptor_set_layout &rr->views[0].mesh.descriptor_set)); // descriptor_set C(create_descriptor_set( // vk, // vk_bundle r->mesh.descriptor_pool, // descriptor_pool r->mesh.descriptor_set_layout, // descriptor_set_layout &rr->views[1].mesh.descriptor_set)); // descriptor_set return true; } void comp_rendering_finalize(struct comp_rendering *rr) { struct vk_bundle *vk = vk_from_rr(rr); VkResult ret; // End the command buffer. ret = vk->vkEndCommandBuffer(rr->cmd); if (ret != VK_SUCCESS) { VK_ERROR(vk, "vkEndCommandBuffer failed: %s", vk_result_string(ret)); return; } } void comp_rendering_close(struct comp_rendering *rr) { struct vk_bundle *vk = vk_from_rr(rr); struct comp_resources *r = rr->r; // Reclaimed by vkResetDescriptorPool. rr->views[0].mesh.descriptor_set = VK_NULL_HANDLE; rr->views[1].mesh.descriptor_set = VK_NULL_HANDLE; vk->vkResetDescriptorPool( // vk->device, // r->mesh.descriptor_pool, // 0); // U_ZERO(rr); } /* * * 'Exported' draw functions. * */ bool comp_draw_begin_target(struct comp_rendering *rr, struct comp_rendering_target_resources *rts) { struct vk_bundle *vk = vk_from_rr(rr); assert(rr->rts == NULL); rr->rts = rts; // This is shared across both views. begin_render_pass(vk, // rr->cmd, // rr->rts->render_pass, // rr->rts->framebuffer, // rr->rts->data.width, // rr->rts->data.height); // return true; } void comp_draw_end_target(struct comp_rendering *rr) { struct vk_bundle *vk = vk_from_rr(rr); assert(rr->rts != NULL); rr->rts = NULL; // Stop the [shared] render pass. vk->vkCmdEndRenderPass(rr->cmd); } void comp_draw_begin_view(struct comp_rendering *rr, uint32_t view, struct comp_viewport_data *viewport_data) { struct vk_bundle *vk = vk_from_rr(rr); // We currently only support two views. assert(view == 0 || view == 1); assert(rr->rts != NULL); rr->current_view = view; /* * Viewport */ VkViewport viewport = { .x = viewport_data->x, .y = viewport_data->y, .width = viewport_data->w, .height = viewport_data->h, .minDepth = 0.0f, .maxDepth = 1.0f, }; vk->vkCmdSetViewport(rr->cmd, // commandBuffer 0, // firstViewport 1, // viewportCount &viewport); // pViewports /* * Scissor */ VkRect2D scissor = { .offset = { .x = viewport_data->x, .y = viewport_data->y, }, .extent = { .width = viewport_data->w, .height = viewport_data->h, }, }; vk->vkCmdSetScissor(rr->cmd, // commandBuffer 0, // firstScissor 1, // scissorCount &scissor); // pScissors } void comp_draw_end_view(struct comp_rendering *rr) { //! Must have a current target. assert(rr->rts != NULL); } void comp_draw_distortion(struct comp_rendering *rr) { struct vk_bundle *vk = vk_from_rr(rr); struct comp_resources *r = rr->r; uint32_t view = rr->current_view; struct comp_rendering_view *v = &rr->views[view]; /* * Descriptors and pipeline. */ VkDescriptorSet descriptor_sets[1] = {v->mesh.descriptor_set}; vk->vkCmdBindDescriptorSets( // rr->cmd, // commandBuffer VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint r->mesh.pipeline_layout, // layout 0, // firstSet ARRAY_SIZE(descriptor_sets), // descriptorSetCount descriptor_sets, // pDescriptorSets 0, // dynamicOffsetCount NULL); // pDynamicOffsets vk->vkCmdBindPipeline( // rr->cmd, // commandBuffer VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint rr->rts->mesh.pipeline); // pipeline /* * Vertex buffer. */ VkBuffer buffers[1] = {r->mesh.vbo.buffer}; VkDeviceSize offsets[1] = {0}; assert(ARRAY_SIZE(buffers) == ARRAY_SIZE(offsets)); vk->vkCmdBindVertexBuffers( // rr->cmd, // commandBuffer 0, // firstBinding ARRAY_SIZE(buffers), // bindingCount buffers, // pBuffers offsets); // pOffsets /* * Draw with indices or not? */ if (r->mesh.total_num_indices > 0) { vk->vkCmdBindIndexBuffer( // rr->cmd, // commandBuffer r->mesh.ibo.buffer, // buffer 0, // offset VK_INDEX_TYPE_UINT32); // indexType vk->vkCmdDrawIndexed( // rr->cmd, // commandBuffer r->mesh.num_indices[view], // indexCount 1, // instanceCount r->mesh.offset_indices[view], // firstIndex 0, // vertexOffset 0); // firstInstance } else { vk->vkCmdDraw( // rr->cmd, // commandBuffer r->mesh.num_vertices, // vertexCount 1, // instanceCount 0, // firstVertex 0); // firstInstance } } void comp_draw_update_distortion(struct comp_rendering *rr, uint32_t view_index, VkSampler sampler, VkImageView image_view, struct comp_mesh_ubo_data *data) { struct vk_bundle *vk = vk_from_rr(rr); struct comp_resources *r = rr->r; struct comp_rendering_view *v = &rr->views[view_index]; comp_buffer_write(vk, &r->mesh.ubos[view_index], data, sizeof(struct comp_mesh_ubo_data)); update_mesh_discriptor_set( // vk, // vk_bundle r->mesh.src_binding, // src_binding sampler, // sampler image_view, // image_view r->mesh.ubo_binding, // ubo_binding r->mesh.ubos[view_index].buffer, // buffer VK_WHOLE_SIZE, // size v->mesh.descriptor_set); // descriptor_set }