#include "CompositorVk.h" #include #include #include #include #include "ErrorLog.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 &code) { VkShaderModuleCreateInfo shaderModuleCi = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = static_cast(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::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(); 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( new ComposeLayerVk(vkSampler, vkImageView, layerTransform)); } Composition::Composition( std::vector> composeLayers) : m_composeLayers(std::move(composeLayers)) {} const std::vector 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 CompositorVk::k_indices = {0, 1, 2, 2, 3, 0}; const VkExtent2D CompositorVk::k_emptyCompositionExtent = {1, 1}; std::unique_ptr CompositorVk::create( const goldfish_vk::VulkanDispatch &vk, VkDevice vkDevice, VkPhysicalDevice vkPhysicalDevice, VkQueue vkQueue, VkFormat format, VkImageLayout initialLayout, VkImageLayout finalLayout, uint32_t width, uint32_t height, const std::vector &renderTargets, VkCommandPool commandPool) { auto res = std::unique_ptr(new CompositorVk( vk, vkDevice, vkPhysicalDevice, vkQueue, commandPool, width, height)); 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> emptyCompositionLayers; res->setComposition(i, std::make_unique( std::move(emptyCompositionLayers))); } return res; } CompositorVk::CompositorVk(const goldfish_vk::VulkanDispatch &vk, VkDevice vkDevice, VkPhysicalDevice vkPhysicalDevice, VkQueue vkQueue, VkCommandPool vkCommandPool, uint32_t renderTargetWidth, uint32_t renderTargetHeight) : CompositorVkBase(vk, vkDevice, vkPhysicalDevice, vkQueue, vkCommandPool), m_renderTargetWidth(renderTargetWidth), m_renderTargetHeight(renderTargetHeight), 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}) { (void)m_renderTargetWidth; (void)m_renderTargetHeight; 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(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 vertSpvBuff( CompositorVkShader::compositorVertexShader, std::end(CompositorVkShader::compositorVertexShader)); const std::vector 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(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(width), .height = static_cast(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(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(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(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> 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 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, [&, 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 &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(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(setsPerDescriptorType), .poolSizeCount = static_cast(std::size(descriptorPoolSizes)), .pPoolSizes = descriptorPoolSizes}; VK_CHECK(m_vk.vkCreateDescriptorPool(m_vkDevice, &descriptorPoolCi, nullptr, &m_vkDescriptorPool)); std::vector 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(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(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, [&, 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(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(&m_uniformStorage.m_data))); } bool CompositorVk::validatePhysicalDeviceFeatures( const VkPhysicalDeviceFeatures2 &features) { auto descIndexingFeatures = vk_find_struct( &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( &features); if (descIndexingFeatures == nullptr) { return false; } descIndexingFeatures->descriptorBindingSampledImageUpdateAfterBind = VK_TRUE; return true; } std::vector 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) { 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(__LINE__), kMaxLayersPerFrame, static_cast(currentComposition.m_composeLayers.size())); } memset(reinterpret_cast(m_uniformStorage.m_data) + (rtIndex * kMaxLayersPerFrame + 0) * m_uniformStorage.m_stride, 0, sizeof(ComposeLayerVk::LayerTransform) * kMaxLayersPerFrame); std::vector imageInfos( currentComposition.m_composeLayers.size()); std::vector 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(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 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)}}; }