| #include "CompositorVk.h" |
| |
| #include <string.h> |
| |
| #include <cinttypes> |
| #include <glm/gtc/matrix_transform.hpp> |
| #include <optional> |
| |
| #include "host-common/logging.h" |
| #include "vulkan/vk_util.h" |
| |
| namespace CompositorVkShader { |
| #include "vulkan/CompositorFragmentShader.h" |
| #include "vulkan/CompositorVertexShader.h" |
| } // namespace CompositorVkShader |
| |
| static VkShaderModule createShaderModule(const goldfish_vk::VulkanDispatch &vk, |
| VkDevice device, |
| const std::vector<uint32_t> &code) { |
| VkShaderModuleCreateInfo shaderModuleCi = { |
| .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, |
| .codeSize = static_cast<uint32_t>(code.size() * sizeof(uint32_t)), |
| .pCode = code.data()}; |
| VkShaderModule res; |
| VK_CHECK(vk.vkCreateShaderModule(device, &shaderModuleCi, nullptr, &res)); |
| return res; |
| } |
| |
| ComposeLayerVk::ComposeLayerVk(VkSampler vkSampler, VkImageView vkImageView, |
| const LayerTransform &layerTransform) |
| : m_vkSampler(vkSampler), |
| m_vkImageView(vkImageView), |
| m_layerTransform( |
| {.pos = layerTransform.pos, .texcoord = layerTransform.texcoord}) {} |
| |
| std::unique_ptr<ComposeLayerVk> ComposeLayerVk::createFromHwc2ComposeLayer( |
| VkSampler vkSampler, VkImageView vkImageView, |
| const ComposeLayer &composeLayer, uint32_t cbWidth, uint32_t cbHeight, |
| uint32_t dstWidth, uint32_t dstHeight) { |
| // Calculate the posTransform and the texcoordTransform needed in the |
| // uniform of the Compositor.vert shader. The posTransform should transform |
| // the square(top = -1, bottom = 1, left = -1, right = 1) to the position |
| // where the layer should be drawn in NDC space given the composeLayer. |
| // texcoordTransform should transform the unit square(top = 0, bottom = 1, |
| // left = 0, right = 1) to where we should sample the layer in the |
| // normalized uv space given the composeLayer. |
| const hwc_rect_t &posRect = composeLayer.displayFrame; |
| const hwc_frect_t &texcoordRect = composeLayer.crop; |
| |
| int posWidth = posRect.right - posRect.left; |
| int posHeight = posRect.bottom - posRect.top; |
| |
| float posScaleX = float(posWidth) / dstWidth; |
| float posScaleY = float(posHeight) / dstHeight; |
| |
| float posTranslateX = |
| -1.0f + posScaleX + 2.0f * float(posRect.left) / dstWidth; |
| float posTranslateY = |
| -1.0f + posScaleY + 2.0f * float(posRect.top) / dstHeight; |
| |
| float texcoordScalX = |
| (texcoordRect.right - texcoordRect.left) / float(cbWidth); |
| float texCoordScaleY = |
| (texcoordRect.bottom - texcoordRect.top) / float(cbHeight); |
| |
| float texCoordTranslateX = texcoordRect.left / float(cbWidth); |
| float texCoordTranslateY = texcoordRect.top / float(cbHeight); |
| |
| float texcoordRotation = 0.0f; |
| |
| const float pi = glm::pi<float>(); |
| |
| switch (composeLayer.transform) { |
| case HWC_TRANSFORM_ROT_90: |
| texcoordRotation = pi * 0.5f; |
| break; |
| case HWC_TRANSFORM_ROT_180: |
| texcoordRotation = pi; |
| break; |
| case HWC_TRANSFORM_ROT_270: |
| texcoordRotation = pi * 1.5f; |
| break; |
| case HWC_TRANSFORM_FLIP_H: |
| texcoordScalX *= -1.0f; |
| break; |
| case HWC_TRANSFORM_FLIP_V: |
| texCoordScaleY *= -1.0f; |
| break; |
| case HWC_TRANSFORM_FLIP_H_ROT_90: |
| texcoordRotation = pi * 0.5f; |
| texcoordScalX *= -1.0f; |
| break; |
| case HWC_TRANSFORM_FLIP_V_ROT_90: |
| texcoordRotation = pi * 0.5f; |
| texCoordScaleY *= -1.0f; |
| break; |
| default: |
| break; |
| } |
| |
| ComposeLayerVk::LayerTransform layerTransform = { |
| .pos = |
| glm::translate(glm::mat4(1.0f), |
| glm::vec3(posTranslateX, posTranslateY, 0.0f)) * |
| glm::scale(glm::mat4(1.0f), glm::vec3(posScaleX, posScaleY, 1.0f)), |
| .texcoord = glm::translate(glm::mat4(1.0f), |
| glm::vec3(texCoordTranslateX, |
| texCoordTranslateY, 0.0f)) * |
| glm::scale(glm::mat4(1.0f), |
| glm::vec3(texcoordScalX, texCoordScaleY, 1.0f)) * |
| glm::rotate(glm::mat4(1.0f), texcoordRotation, |
| glm::vec3(0.0f, 0.0f, 1.0f)), |
| }; |
| |
| return std::unique_ptr<ComposeLayerVk>( |
| new ComposeLayerVk(vkSampler, vkImageView, layerTransform)); |
| } |
| |
| Composition::Composition( |
| std::vector<std::unique_ptr<ComposeLayerVk>> composeLayers) |
| : m_composeLayers(std::move(composeLayers)) {} |
| |
| const std::vector<CompositorVk::Vertex> CompositorVk::k_vertices = { |
| {{-1.0f, -1.0f}, {0.0f, 0.0f}}, |
| {{1.0f, -1.0f}, {1.0f, 0.0f}}, |
| {{1.0f, 1.0f}, {1.0f, 1.0f}}, |
| {{-1.0f, 1.0f}, {0.0f, 1.0f}}, |
| }; |
| |
| const std::vector<uint16_t> CompositorVk::k_indices = {0, 1, 2, 2, 3, 0}; |
| |
| const VkExtent2D CompositorVk::k_emptyCompositionExtent = {1, 1}; |
| |
| std::unique_ptr<CompositorVk> CompositorVk::create( |
| const goldfish_vk::VulkanDispatch &vk, VkDevice vkDevice, |
| VkPhysicalDevice vkPhysicalDevice, VkQueue vkQueue, |
| std::shared_ptr<android::base::Lock> queueLock, VkFormat format, |
| VkImageLayout initialLayout, VkImageLayout finalLayout, uint32_t width, |
| uint32_t height, const std::vector<VkImageView> &renderTargets, |
| VkCommandPool commandPool) { |
| auto res = std::unique_ptr<CompositorVk>(new CompositorVk( |
| vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, commandPool)); |
| res->setUpGraphicsPipeline(width, height, format, initialLayout, |
| finalLayout); |
| res->setUpVertexBuffers(); |
| res->setUpFramebuffers(renderTargets, width, height); |
| res->setUpUniformBuffers(); |
| res->setUpDescriptorSets(); |
| res->setUpCommandBuffers(width, height); |
| res->setUpEmptyComposition(format); |
| res->m_currentCompositions.resize(renderTargets.size()); |
| for (auto i = 0; i < renderTargets.size(); i++) { |
| std::vector<std::unique_ptr<ComposeLayerVk>> emptyCompositionLayers; |
| res->setComposition(i, std::make_unique<Composition>( |
| std::move(emptyCompositionLayers))); |
| } |
| return res; |
| } |
| |
| CompositorVk::CompositorVk(const goldfish_vk::VulkanDispatch &vk, |
| VkDevice vkDevice, VkPhysicalDevice vkPhysicalDevice, |
| VkQueue vkQueue, |
| std::shared_ptr<android::base::Lock> queueLock, |
| VkCommandPool vkCommandPool) |
| : CompositorVkBase(vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, |
| vkCommandPool), |
| m_emptyCompositionVkImage(VK_NULL_HANDLE), |
| m_emptyCompositionVkDeviceMemory(VK_NULL_HANDLE), |
| m_emptyCompositionVkImageView(VK_NULL_HANDLE), |
| m_emptyCompositionVkSampler(VK_NULL_HANDLE), |
| m_currentCompositions(0), |
| m_uniformStorage({VK_NULL_HANDLE, VK_NULL_HANDLE, nullptr, 0}) { |
| VkPhysicalDeviceProperties physicalDeviceProperties; |
| m_vk.vkGetPhysicalDeviceProperties(m_vkPhysicalDevice, |
| &physicalDeviceProperties); |
| auto alignment = |
| physicalDeviceProperties.limits.minUniformBufferOffsetAlignment; |
| m_uniformStorage.m_stride = |
| ((sizeof(UniformBufferObject) - 1) / alignment + 1) * alignment; |
| } |
| |
| CompositorVk::~CompositorVk() { |
| m_vk.vkDestroySampler(m_vkDevice, m_emptyCompositionVkSampler, nullptr); |
| m_vk.vkDestroyImageView(m_vkDevice, m_emptyCompositionVkImageView, nullptr); |
| m_vk.vkFreeMemory(m_vkDevice, m_emptyCompositionVkDeviceMemory, nullptr); |
| m_vk.vkDestroyImage(m_vkDevice, m_emptyCompositionVkImage, nullptr); |
| m_vk.vkDestroyDescriptorPool(m_vkDevice, m_vkDescriptorPool, nullptr); |
| m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool, |
| static_cast<uint32_t>(m_vkCommandBuffers.size()), |
| m_vkCommandBuffers.data()); |
| if (m_uniformStorage.m_vkDeviceMemory != VK_NULL_HANDLE) { |
| m_vk.vkUnmapMemory(m_vkDevice, m_uniformStorage.m_vkDeviceMemory); |
| } |
| m_vk.vkDestroyBuffer(m_vkDevice, m_uniformStorage.m_vkBuffer, nullptr); |
| m_vk.vkFreeMemory(m_vkDevice, m_uniformStorage.m_vkDeviceMemory, nullptr); |
| while (!m_renderTargetVkFrameBuffers.empty()) { |
| m_vk.vkDestroyFramebuffer(m_vkDevice, |
| m_renderTargetVkFrameBuffers.back(), nullptr); |
| m_renderTargetVkFrameBuffers.pop_back(); |
| } |
| m_vk.vkFreeMemory(m_vkDevice, m_vertexVkDeviceMemory, nullptr); |
| m_vk.vkDestroyBuffer(m_vkDevice, m_vertexVkBuffer, nullptr); |
| m_vk.vkFreeMemory(m_vkDevice, m_indexVkDeviceMemory, nullptr); |
| m_vk.vkDestroyBuffer(m_vkDevice, m_indexVkBuffer, nullptr); |
| m_vk.vkDestroyPipeline(m_vkDevice, m_graphicsVkPipeline, nullptr); |
| m_vk.vkDestroyRenderPass(m_vkDevice, m_vkRenderPass, nullptr); |
| m_vk.vkDestroyPipelineLayout(m_vkDevice, m_vkPipelineLayout, nullptr); |
| m_vk.vkDestroyDescriptorSetLayout(m_vkDevice, m_vkDescriptorSetLayout, |
| nullptr); |
| } |
| |
| void CompositorVk::setUpGraphicsPipeline(uint32_t width, uint32_t height, |
| VkFormat renderTargetFormat, |
| VkImageLayout initialLayout, |
| VkImageLayout finalLayout) { |
| const std::vector<uint32_t> vertSpvBuff( |
| CompositorVkShader::compositorVertexShader, |
| std::end(CompositorVkShader::compositorVertexShader)); |
| const std::vector<uint32_t> fragSpvBuff( |
| CompositorVkShader::compositorFragmentShader, |
| std::end(CompositorVkShader::compositorFragmentShader)); |
| const auto vertShaderMod = |
| createShaderModule(m_vk, m_vkDevice, vertSpvBuff); |
| const auto fragShaderMod = |
| createShaderModule(m_vk, m_vkDevice, fragSpvBuff); |
| |
| VkPipelineShaderStageCreateInfo shaderStageCis[2] = { |
| {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| .stage = VK_SHADER_STAGE_VERTEX_BIT, |
| .module = vertShaderMod, |
| .pName = "main"}, |
| {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| .stage = VK_SHADER_STAGE_FRAGMENT_BIT, |
| .module = fragShaderMod, |
| .pName = "main"}}; |
| |
| auto bindingDescription = Vertex::getBindingDescription(); |
| auto attributeDescription = Vertex::getAttributeDescription(); |
| VkPipelineVertexInputStateCreateInfo vertexInputStateCi = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, |
| .vertexBindingDescriptionCount = 1, |
| .pVertexBindingDescriptions = &bindingDescription, |
| .vertexAttributeDescriptionCount = |
| static_cast<uint32_t>(attributeDescription.size()), |
| .pVertexAttributeDescriptions = attributeDescription.data()}; |
| VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCi = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, |
| .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, |
| .primitiveRestartEnable = VK_FALSE, |
| }; |
| |
| VkViewport viewport = {.x = 0.0f, |
| .y = 0.0f, |
| .width = static_cast<float>(width), |
| .height = static_cast<float>(height), |
| .minDepth = 0.0f, |
| .maxDepth = 1.0f}; |
| |
| VkRect2D scissor = {.offset = {0, 0}, .extent = {width, height}}; |
| |
| VkPipelineViewportStateCreateInfo viewportStateCi = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, |
| .viewportCount = 1, |
| .pViewports = &viewport, |
| .scissorCount = 1, |
| .pScissors = &scissor}; |
| |
| VkPipelineRasterizationStateCreateInfo rasterizerStateCi = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, |
| .depthClampEnable = VK_FALSE, |
| .rasterizerDiscardEnable = VK_FALSE, |
| .polygonMode = VK_POLYGON_MODE_FILL, |
| .cullMode = VK_CULL_MODE_BACK_BIT, |
| .frontFace = VK_FRONT_FACE_CLOCKWISE, |
| .depthBiasEnable = VK_FALSE, |
| .depthBiasConstantFactor = 0.0f, |
| .depthBiasClamp = 0.0f, |
| .depthBiasSlopeFactor = 0.0f, |
| .lineWidth = 1.0f}; |
| |
| VkPipelineMultisampleStateCreateInfo multisampleStateCi = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, |
| .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, |
| .sampleShadingEnable = VK_FALSE, |
| .minSampleShading = 1.0f, |
| .pSampleMask = nullptr, |
| .alphaToCoverageEnable = VK_FALSE, |
| .alphaToOneEnable = VK_FALSE}; |
| |
| VkPipelineColorBlendAttachmentState colorBlendAttachment = { |
| .blendEnable = VK_TRUE, |
| .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, |
| .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, |
| .colorBlendOp = VK_BLEND_OP_ADD, |
| .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, |
| .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, |
| .alphaBlendOp = VK_BLEND_OP_ADD, |
| .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | |
| VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT}; |
| |
| VkPipelineColorBlendStateCreateInfo colorBlendStateCi = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, |
| .logicOpEnable = VK_FALSE, |
| .attachmentCount = 1, |
| .pAttachments = &colorBlendAttachment}; |
| |
| VkDescriptorSetLayoutBinding layoutBindings[2] = { |
| {.binding = 0, |
| .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, |
| .descriptorCount = 1, |
| .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, |
| .pImmutableSamplers = nullptr}, |
| {.binding = 1, |
| .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| .descriptorCount = 1, |
| .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, |
| .pImmutableSamplers = nullptr}}; |
| |
| VkDescriptorBindingFlagsEXT bindingFlags[] = { |
| VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT, 0}; |
| VkDescriptorSetLayoutBindingFlagsCreateInfoEXT bindingFlagsCi = { |
| .sType = |
| VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT, |
| .bindingCount = static_cast<uint32_t>(std::size(bindingFlags)), |
| .pBindingFlags = bindingFlags, |
| }; |
| VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCi = { |
| .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, |
| .pNext = &bindingFlagsCi, |
| .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT, |
| .bindingCount = static_cast<uint32_t>(std::size(layoutBindings)), |
| .pBindings = layoutBindings}; |
| VK_CHECK(m_vk.vkCreateDescriptorSetLayout( |
| m_vkDevice, &descriptorSetLayoutCi, nullptr, &m_vkDescriptorSetLayout)); |
| |
| VkPipelineLayoutCreateInfo pipelineLayoutCi = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, |
| .setLayoutCount = 1, |
| .pSetLayouts = &m_vkDescriptorSetLayout, |
| .pushConstantRangeCount = 0}; |
| |
| VK_CHECK(m_vk.vkCreatePipelineLayout(m_vkDevice, &pipelineLayoutCi, nullptr, |
| &m_vkPipelineLayout)); |
| |
| VkAttachmentDescription colorAttachment = { |
| .format = renderTargetFormat, |
| .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 = initialLayout, |
| .finalLayout = finalLayout}; |
| |
| VkAttachmentReference colorAttachmentRef = { |
| .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; |
| |
| VkSubpassDescription subpass = { |
| .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, |
| .colorAttachmentCount = 1, |
| .pColorAttachments = &colorAttachmentRef}; |
| |
| // TODO: to support multiple layer composition, we could run the same render |
| // pass for multiple time. In that case, we should use explicit |
| // VkImageMemoryBarriers to transform the image layout instead of relying on |
| // renderpass to do it. |
| VkSubpassDependency subpassDependency = { |
| .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_WRITE_BIT}; |
| |
| VkRenderPassCreateInfo renderPassCi = { |
| .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, |
| .attachmentCount = 1, |
| .pAttachments = &colorAttachment, |
| .subpassCount = 1, |
| .pSubpasses = &subpass, |
| .dependencyCount = 1, |
| .pDependencies = &subpassDependency}; |
| |
| VK_CHECK(m_vk.vkCreateRenderPass(m_vkDevice, &renderPassCi, nullptr, |
| &m_vkRenderPass)); |
| |
| VkGraphicsPipelineCreateInfo graphicsPipelineCi = { |
| .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, |
| .stageCount = static_cast<uint32_t>(std::size(shaderStageCis)), |
| .pStages = shaderStageCis, |
| .pVertexInputState = &vertexInputStateCi, |
| .pInputAssemblyState = &inputAssemblyStateCi, |
| .pViewportState = &viewportStateCi, |
| .pRasterizationState = &rasterizerStateCi, |
| .pMultisampleState = &multisampleStateCi, |
| .pDepthStencilState = nullptr, |
| .pColorBlendState = &colorBlendStateCi, |
| .pDynamicState = nullptr, |
| .layout = m_vkPipelineLayout, |
| .renderPass = m_vkRenderPass, |
| .subpass = 0, |
| .basePipelineHandle = VK_NULL_HANDLE, |
| .basePipelineIndex = -1}; |
| |
| VK_CHECK(m_vk.vkCreateGraphicsPipelines(m_vkDevice, VK_NULL_HANDLE, 1, |
| &graphicsPipelineCi, nullptr, |
| &m_graphicsVkPipeline)); |
| |
| m_vk.vkDestroyShaderModule(m_vkDevice, vertShaderMod, nullptr); |
| m_vk.vkDestroyShaderModule(m_vkDevice, fragShaderMod, nullptr); |
| } |
| |
| // Create a VkBuffer and a bound VkDeviceMemory. When the specified memory type |
| // can't be found, return std::nullopt. When Vulkan call fails, terminate the |
| // program. |
| std::optional<std::tuple<VkBuffer, VkDeviceMemory>> CompositorVk::createBuffer( |
| VkDeviceSize size, VkBufferUsageFlags usage, |
| VkMemoryPropertyFlags memProperty) const { |
| VkBufferCreateInfo bufferCi = { |
| .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, |
| .size = size, |
| .usage = usage, |
| .sharingMode = VK_SHARING_MODE_EXCLUSIVE}; |
| VkBuffer resBuffer; |
| VK_CHECK(m_vk.vkCreateBuffer(m_vkDevice, &bufferCi, nullptr, &resBuffer)); |
| VkMemoryRequirements memRequirements; |
| m_vk.vkGetBufferMemoryRequirements(m_vkDevice, resBuffer, &memRequirements); |
| VkPhysicalDeviceMemoryProperties physicalMemProperties; |
| m_vk.vkGetPhysicalDeviceMemoryProperties(m_vkPhysicalDevice, |
| &physicalMemProperties); |
| auto maybeMemoryTypeIndex = |
| findMemoryType(memRequirements.memoryTypeBits, memProperty); |
| if (!maybeMemoryTypeIndex.has_value()) { |
| m_vk.vkDestroyBuffer(m_vkDevice, resBuffer, nullptr); |
| return std::nullopt; |
| } |
| VkMemoryAllocateInfo memAllocInfo = { |
| .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
| .allocationSize = memRequirements.size, |
| .memoryTypeIndex = maybeMemoryTypeIndex.value()}; |
| VkDeviceMemory resMemory; |
| VK_CHECK( |
| m_vk.vkAllocateMemory(m_vkDevice, &memAllocInfo, nullptr, &resMemory)); |
| VK_CHECK(m_vk.vkBindBufferMemory(m_vkDevice, resBuffer, resMemory, 0)); |
| return std::make_tuple(resBuffer, resMemory); |
| } |
| |
| std::tuple<VkBuffer, VkDeviceMemory> CompositorVk::createStagingBufferWithData( |
| const void *srcData, VkDeviceSize size) const { |
| auto [stagingBuffer, stagingBufferMemory] = |
| createBuffer(size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) |
| .value(); |
| void *data; |
| VK_CHECK( |
| m_vk.vkMapMemory(m_vkDevice, stagingBufferMemory, 0, size, 0, &data)); |
| memcpy(data, srcData, size); |
| m_vk.vkUnmapMemory(m_vkDevice, stagingBufferMemory); |
| return std::make_tuple(stagingBuffer, stagingBufferMemory); |
| } |
| |
| void CompositorVk::copyBuffer(VkBuffer src, VkBuffer dst, |
| VkDeviceSize size) const { |
| runSingleTimeCommands( |
| m_vkQueue, m_vkQueueLock, [&, this](const auto &cmdBuff) { |
| VkBufferCopy copyRegion = {}; |
| copyRegion.srcOffset = 0; |
| copyRegion.dstOffset = 0; |
| copyRegion.size = size; |
| m_vk.vkCmdCopyBuffer(cmdBuff, src, dst, 1, ©Region); |
| }); |
| } |
| |
| void CompositorVk::setUpVertexBuffers() { |
| const VkDeviceSize vertexBufferSize = |
| sizeof(k_vertices[0]) * k_vertices.size(); |
| std::tie(m_vertexVkBuffer, m_vertexVkDeviceMemory) = |
| createBuffer(vertexBufferSize, |
| VK_BUFFER_USAGE_TRANSFER_DST_BIT | |
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) |
| .value(); |
| auto [vertexStagingBuffer, vertexStagingBufferMemory] = |
| createStagingBufferWithData(k_vertices.data(), vertexBufferSize); |
| copyBuffer(vertexStagingBuffer, m_vertexVkBuffer, vertexBufferSize); |
| m_vk.vkDestroyBuffer(m_vkDevice, vertexStagingBuffer, nullptr); |
| m_vk.vkFreeMemory(m_vkDevice, vertexStagingBufferMemory, nullptr); |
| |
| VkDeviceSize indexBufferSize = sizeof(k_indices[0]) * k_indices.size(); |
| auto [indexStagingBuffer, indexStagingBufferMemory] = |
| createStagingBufferWithData(k_indices.data(), indexBufferSize); |
| std::tie(m_indexVkBuffer, m_indexVkDeviceMemory) = |
| createBuffer( |
| indexBufferSize, |
| VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) |
| .value(); |
| copyBuffer(indexStagingBuffer, m_indexVkBuffer, indexBufferSize); |
| m_vk.vkDestroyBuffer(m_vkDevice, indexStagingBuffer, nullptr); |
| m_vk.vkFreeMemory(m_vkDevice, indexStagingBufferMemory, nullptr); |
| } |
| |
| void CompositorVk::setUpFramebuffers( |
| const std::vector<VkImageView> &renderTargets, uint32_t width, |
| uint32_t height) { |
| for (size_t i = 0; i < renderTargets.size(); i++) { |
| VkFramebufferCreateInfo fbCi = { |
| .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, |
| .renderPass = m_vkRenderPass, |
| .attachmentCount = 1, |
| .pAttachments = &renderTargets[i], |
| .width = width, |
| .height = height, |
| .layers = 1}; |
| VkFramebuffer framebuffer; |
| VK_CHECK( |
| m_vk.vkCreateFramebuffer(m_vkDevice, &fbCi, nullptr, &framebuffer)); |
| m_renderTargetVkFrameBuffers.push_back(framebuffer); |
| } |
| } |
| |
| // We don't see composition requests with more than 10 layers from the guest for |
| // now. If we see rendering error or significant time spent on updating |
| // descriptors in setComposition, we should tune this number. |
| static const uint32_t kMaxLayersPerFrame = 10; |
| |
| void CompositorVk::setUpDescriptorSets() { |
| uint32_t numOfFrames = |
| static_cast<uint32_t>(m_renderTargetVkFrameBuffers.size()); |
| |
| uint32_t setsPerDescriptorType = numOfFrames * kMaxLayersPerFrame; |
| |
| VkDescriptorPoolSize descriptorPoolSizes[2] = { |
| {.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, |
| .descriptorCount = setsPerDescriptorType}, |
| {.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| .descriptorCount = setsPerDescriptorType}}; |
| |
| VkDescriptorPoolCreateInfo descriptorPoolCi = { |
| .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, |
| .flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT, |
| .maxSets = static_cast<uint32_t>(setsPerDescriptorType), |
| .poolSizeCount = static_cast<uint32_t>(std::size(descriptorPoolSizes)), |
| .pPoolSizes = descriptorPoolSizes}; |
| VK_CHECK(m_vk.vkCreateDescriptorPool(m_vkDevice, &descriptorPoolCi, nullptr, |
| &m_vkDescriptorPool)); |
| std::vector<VkDescriptorSetLayout> layouts(setsPerDescriptorType, |
| m_vkDescriptorSetLayout); |
| VkDescriptorSetAllocateInfo descriptorSetAllocInfo = { |
| .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, |
| .descriptorPool = m_vkDescriptorPool, |
| .descriptorSetCount = setsPerDescriptorType, |
| .pSetLayouts = layouts.data()}; |
| m_vkDescriptorSets.resize(setsPerDescriptorType); |
| VK_CHECK(m_vk.vkAllocateDescriptorSets(m_vkDevice, &descriptorSetAllocInfo, |
| m_vkDescriptorSets.data())); |
| for (size_t i = 0; i < setsPerDescriptorType; i++) { |
| VkDescriptorBufferInfo bufferInfo = { |
| .buffer = m_uniformStorage.m_vkBuffer, |
| .offset = i * m_uniformStorage.m_stride, |
| .range = sizeof(UniformBufferObject)}; |
| VkWriteDescriptorSet descriptorSetWrite = { |
| .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, |
| .dstSet = m_vkDescriptorSets[i], |
| .dstBinding = 1, |
| .dstArrayElement = 0, |
| .descriptorCount = 1, |
| .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| .pBufferInfo = &bufferInfo}; |
| m_vk.vkUpdateDescriptorSets(m_vkDevice, 1, &descriptorSetWrite, 0, |
| nullptr); |
| } |
| } |
| |
| void CompositorVk::setUpCommandBuffers(uint32_t width, uint32_t height) { |
| VkCommandBufferAllocateInfo cmdBuffAllocInfo = { |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, |
| .commandPool = m_vkCommandPool, |
| .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, |
| .commandBufferCount = |
| static_cast<uint32_t>(m_renderTargetVkFrameBuffers.size())}; |
| m_vkCommandBuffers.resize(m_renderTargetVkFrameBuffers.size()); |
| VK_CHECK(m_vk.vkAllocateCommandBuffers(m_vkDevice, &cmdBuffAllocInfo, |
| m_vkCommandBuffers.data())); |
| |
| for (size_t i = 0; i < m_vkCommandBuffers.size(); i++) { |
| VkCommandBufferBeginInfo beginInfo = { |
| beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO}; |
| const auto &cmdBuffer = m_vkCommandBuffers[i]; |
| VK_CHECK(m_vk.vkBeginCommandBuffer(cmdBuffer, &beginInfo)); |
| |
| VkClearValue clearColor = { |
| .color = {.float32 = {0.0f, 0.0f, 0.0f, 1.0f}}}; |
| VkRenderPassBeginInfo renderPassBeginInfo = { |
| .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, |
| .renderPass = m_vkRenderPass, |
| .framebuffer = m_renderTargetVkFrameBuffers[i], |
| .renderArea = {.offset = {0, 0}, .extent = {width, height}}, |
| .clearValueCount = 1, |
| .pClearValues = &clearColor}; |
| m_vk.vkCmdBeginRenderPass(cmdBuffer, &renderPassBeginInfo, |
| VK_SUBPASS_CONTENTS_INLINE); |
| m_vk.vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, |
| m_graphicsVkPipeline); |
| VkDeviceSize offsets[] = {0}; |
| m_vk.vkCmdBindVertexBuffers(cmdBuffer, 0, 1, &m_vertexVkBuffer, |
| offsets); |
| m_vk.vkCmdBindIndexBuffer(cmdBuffer, m_indexVkBuffer, 0, |
| VK_INDEX_TYPE_UINT16); |
| for (uint32_t j = 0; j < kMaxLayersPerFrame; ++j) { |
| m_vk.vkCmdBindDescriptorSets( |
| cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_vkPipelineLayout, |
| 0, 1, &m_vkDescriptorSets[i * kMaxLayersPerFrame + j], 0, |
| nullptr); |
| m_vk.vkCmdDrawIndexed( |
| cmdBuffer, static_cast<uint32_t>(k_indices.size()), 1, 0, 0, 0); |
| } |
| m_vk.vkCmdEndRenderPass(cmdBuffer); |
| |
| VK_CHECK(m_vk.vkEndCommandBuffer(cmdBuffer)); |
| } |
| } |
| |
| void CompositorVk::setUpEmptyComposition(VkFormat format) { |
| VkImageCreateInfo imageCi = { |
| .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |
| .flags = 0, |
| .imageType = VK_IMAGE_TYPE_2D, |
| .format = format, |
| .extent = {.width = k_emptyCompositionExtent.width, |
| .height = k_emptyCompositionExtent.height, |
| .depth = 1}, |
| .mipLevels = 1, |
| .arrayLayers = 1, |
| .samples = VK_SAMPLE_COUNT_1_BIT, |
| .tiling = VK_IMAGE_TILING_OPTIMAL, |
| .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, |
| .sharingMode = VK_SHARING_MODE_EXCLUSIVE, |
| .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED}; |
| VK_CHECK(m_vk.vkCreateImage(m_vkDevice, &imageCi, nullptr, |
| &m_emptyCompositionVkImage)); |
| |
| VkMemoryRequirements memRequirements; |
| m_vk.vkGetImageMemoryRequirements(m_vkDevice, m_emptyCompositionVkImage, |
| &memRequirements); |
| VkMemoryAllocateInfo allocInfo = { |
| .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
| .allocationSize = memRequirements.size, |
| .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) |
| .value()}; |
| VK_CHECK(m_vk.vkAllocateMemory(m_vkDevice, &allocInfo, nullptr, |
| &m_emptyCompositionVkDeviceMemory)); |
| VK_CHECK(m_vk.vkBindImageMemory(m_vkDevice, m_emptyCompositionVkImage, |
| m_emptyCompositionVkDeviceMemory, 0)); |
| runSingleTimeCommands( |
| m_vkQueue, m_vkQueueLock, [&, this](const auto &cmdBuff) { |
| recordImageLayoutTransformCommands( |
| cmdBuff, m_emptyCompositionVkImage, VK_IMAGE_LAYOUT_UNDEFINED, |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); |
| VkClearColorValue clearColor = {.float32 = {0.0, 0.0, 0.0, 1.0}}; |
| VkImageSubresourceRange range = { |
| .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .baseMipLevel = 0, |
| .levelCount = 1, |
| .baseArrayLayer = 0, |
| .layerCount = 1}; |
| m_vk.vkCmdClearColorImage(cmdBuff, m_emptyCompositionVkImage, |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, |
| &clearColor, 1, &range); |
| recordImageLayoutTransformCommands( |
| cmdBuff, m_emptyCompositionVkImage, |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, |
| VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); |
| }); |
| |
| VkImageViewCreateInfo imageViewCi = { |
| .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
| .image = m_emptyCompositionVkImage, |
| .viewType = VK_IMAGE_VIEW_TYPE_2D, |
| .format = format, |
| .components = {.r = VK_COMPONENT_SWIZZLE_IDENTITY, |
| .g = VK_COMPONENT_SWIZZLE_IDENTITY, |
| .b = VK_COMPONENT_SWIZZLE_IDENTITY, |
| .a = VK_COMPONENT_SWIZZLE_IDENTITY}, |
| .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .baseMipLevel = 0, |
| .levelCount = 1, |
| .baseArrayLayer = 0, |
| .layerCount = 1}}; |
| VK_CHECK(m_vk.vkCreateImageView(m_vkDevice, &imageViewCi, nullptr, |
| &m_emptyCompositionVkImageView)); |
| |
| VkSamplerCreateInfo samplerCi = { |
| .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, |
| .magFilter = VK_FILTER_LINEAR, |
| .minFilter = VK_FILTER_LINEAR, |
| .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, |
| .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, |
| .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, |
| .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, |
| .mipLodBias = 0.0f, |
| .anisotropyEnable = VK_FALSE, |
| .maxAnisotropy = 1.0f, |
| .compareEnable = VK_FALSE, |
| .compareOp = VK_COMPARE_OP_ALWAYS, |
| .minLod = 0.0f, |
| .maxLod = 0.0f, |
| .borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK, |
| .unnormalizedCoordinates = VK_FALSE}; |
| VK_CHECK(m_vk.vkCreateSampler(m_vkDevice, &samplerCi, nullptr, |
| &m_emptyCompositionVkSampler)); |
| } |
| |
| void CompositorVk::setUpUniformBuffers() { |
| auto numOfFrames = m_renderTargetVkFrameBuffers.size(); |
| VkDeviceSize size = |
| m_uniformStorage.m_stride * numOfFrames * kMaxLayersPerFrame; |
| auto maybeBuffer = createBuffer(size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | |
| VK_MEMORY_PROPERTY_HOST_CACHED_BIT); |
| auto buffer = std::make_tuple<VkBuffer, VkDeviceMemory>(VK_NULL_HANDLE, |
| VK_NULL_HANDLE); |
| if (maybeBuffer.has_value()) { |
| buffer = maybeBuffer.value(); |
| } else { |
| buffer = createBuffer(size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) |
| .value(); |
| } |
| std::tie(m_uniformStorage.m_vkBuffer, m_uniformStorage.m_vkDeviceMemory) = |
| buffer; |
| VK_CHECK(m_vk.vkMapMemory( |
| m_vkDevice, m_uniformStorage.m_vkDeviceMemory, 0, size, 0, |
| reinterpret_cast<void **>(&m_uniformStorage.m_data))); |
| } |
| |
| bool CompositorVk::validatePhysicalDeviceFeatures( |
| const VkPhysicalDeviceFeatures2 &features) { |
| auto descIndexingFeatures = |
| vk_find_struct<VkPhysicalDeviceDescriptorIndexingFeaturesEXT>( |
| &features); |
| if (descIndexingFeatures == nullptr) { |
| return false; |
| } |
| return descIndexingFeatures->descriptorBindingSampledImageUpdateAfterBind == |
| VK_TRUE; |
| } |
| |
| bool CompositorVk::validateQueueFamilyProperties( |
| const VkQueueFamilyProperties &properties) { |
| return properties.queueFlags & VK_QUEUE_GRAPHICS_BIT; |
| } |
| |
| bool CompositorVk::enablePhysicalDeviceFeatures( |
| VkPhysicalDeviceFeatures2 &features) { |
| auto descIndexingFeatures = |
| vk_find_struct<VkPhysicalDeviceDescriptorIndexingFeaturesEXT>( |
| &features); |
| if (descIndexingFeatures == nullptr) { |
| return false; |
| } |
| descIndexingFeatures->descriptorBindingSampledImageUpdateAfterBind = |
| VK_TRUE; |
| return true; |
| } |
| |
| std::vector<const char *> CompositorVk::getRequiredDeviceExtensions() { |
| return {VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME}; |
| } |
| |
| VkCommandBuffer CompositorVk::getCommandBuffer(uint32_t i) const { |
| return m_vkCommandBuffers[i]; |
| } |
| |
| void CompositorVk::setComposition(uint32_t rtIndex, |
| std::unique_ptr<Composition> &&composition) { |
| m_currentCompositions[rtIndex] = std::move(composition); |
| const auto ¤tComposition = *m_currentCompositions[rtIndex]; |
| if (currentComposition.m_composeLayers.size() > kMaxLayersPerFrame) { |
| ERR("%s(%s:%d): CompositorVk can't compose more than %" PRIu32 |
| " layers, %" PRIu32 " layers asked.\n", |
| __FUNCTION__, __FILE__, static_cast<int>(__LINE__), |
| kMaxLayersPerFrame, |
| static_cast<uint32_t>(currentComposition.m_composeLayers.size())); |
| } |
| |
| memset(reinterpret_cast<uint8_t *>(m_uniformStorage.m_data) + |
| (rtIndex * kMaxLayersPerFrame + 0) * m_uniformStorage.m_stride, |
| 0, sizeof(ComposeLayerVk::LayerTransform) * kMaxLayersPerFrame); |
| |
| std::vector<VkDescriptorImageInfo> imageInfos( |
| currentComposition.m_composeLayers.size()); |
| std::vector<VkWriteDescriptorSet> descriptorWrites; |
| for (size_t i = 0; i < currentComposition.m_composeLayers.size(); ++i) { |
| const auto &layer = currentComposition.m_composeLayers[i]; |
| imageInfos[i] = VkDescriptorImageInfo( |
| {.sampler = layer->m_vkSampler, |
| .imageView = layer->m_vkImageView, |
| .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}); |
| const VkDescriptorImageInfo &imageInfo = imageInfos[i]; |
| descriptorWrites.emplace_back(VkWriteDescriptorSet( |
| {.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, |
| .dstSet = m_vkDescriptorSets[rtIndex * kMaxLayersPerFrame + i], |
| .dstBinding = 0, |
| .dstArrayElement = 0, |
| .descriptorCount = 1, |
| .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, |
| .pImageInfo = &imageInfo})); |
| memcpy( |
| reinterpret_cast<uint8_t *>(m_uniformStorage.m_data) + |
| (rtIndex * kMaxLayersPerFrame + i) * m_uniformStorage.m_stride, |
| &layer->m_layerTransform, sizeof(ComposeLayerVk::LayerTransform)); |
| } |
| m_vk.vkUpdateDescriptorSets(m_vkDevice, descriptorWrites.size(), |
| descriptorWrites.data(), 0, nullptr); |
| |
| for (size_t i = currentComposition.m_composeLayers.size(); |
| i < kMaxLayersPerFrame; ++i) { |
| VkDescriptorImageInfo imageInfo = { |
| .sampler = m_emptyCompositionVkSampler, |
| .imageView = m_emptyCompositionVkImageView, |
| .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}; |
| VkWriteDescriptorSet descriptorSetWrite = { |
| .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, |
| .dstSet = m_vkDescriptorSets[rtIndex * kMaxLayersPerFrame + i], |
| .dstBinding = 0, |
| .dstArrayElement = 0, |
| .descriptorCount = 1, |
| .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, |
| .pImageInfo = &imageInfo}; |
| m_vk.vkUpdateDescriptorSets(m_vkDevice, 1, &descriptorSetWrite, 0, |
| nullptr); |
| } |
| } |
| |
| VkVertexInputBindingDescription CompositorVk::Vertex::getBindingDescription() { |
| return {.binding = 0, |
| .stride = sizeof(struct Vertex), |
| .inputRate = VK_VERTEX_INPUT_RATE_VERTEX}; |
| } |
| |
| std::array<VkVertexInputAttributeDescription, 2> |
| CompositorVk::Vertex::getAttributeDescription() { |
| return {VkVertexInputAttributeDescription{ |
| .location = 0, |
| .binding = 0, |
| .format = VK_FORMAT_R32G32_SFLOAT, |
| .offset = offsetof(struct Vertex, pos)}, |
| VkVertexInputAttributeDescription{ |
| .location = 1, |
| .binding = 0, |
| .format = VK_FORMAT_R32G32_SFLOAT, |
| .offset = offsetof(struct Vertex, texPos)}}; |
| } |