| #include "CompositorVk.h" |
| |
| #include <string.h> |
| |
| #include <cinttypes> |
| #include <glm/gtc/matrix_transform.hpp> |
| #include <optional> |
| |
| #include "host-common/logging.h" |
| #include "vulkan/VkCommonOperations.h" |
| #include "vulkan/vk_util.h" |
| |
| using emugl::ABORT_REASON_OTHER; |
| using emugl::FatalError; |
| |
| namespace CompositorVkShader { |
| #include "vulkan/CompositorFragmentShader.h" |
| #include "vulkan/CompositorVertexShader.h" |
| } // namespace CompositorVkShader |
| |
| #define COMPOSITOR_VK_ERROR(fmt, ...) \ |
| do { \ |
| fprintf(stderr, "%s(%s:%d): " fmt "\n", __func__, __FILE__, __LINE__, ##__VA_ARGS__); \ |
| fflush(stderr); \ |
| } while (0) |
| |
| 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}; |
| |
| 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 maxFramesInFlight, |
| VkCommandPool commandPool, VkSampler sampler) { |
| auto res = std::unique_ptr<CompositorVk>(new CompositorVk( |
| vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, commandPool, maxFramesInFlight)); |
| res->setUpGraphicsPipeline(format, initialLayout, finalLayout, sampler); |
| res->m_vkSampler = sampler; |
| res->setUpVertexBuffers(); |
| res->setUpUniformBuffers(); |
| res->setUpDescriptorSets(); |
| res->m_currentCompositions.resize(maxFramesInFlight); |
| for (auto i = 0; i < maxFramesInFlight; 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 commandPool, uint32_t maxFramesInFlight) |
| : CompositorVkBase(vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, commandPool), |
| m_maxFramesInFlight(maxFramesInFlight), |
| m_vkSampler(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.vkDestroyDescriptorPool(m_vkDevice, m_vkDescriptorPool, nullptr); |
| 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); |
| 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(VkFormat renderTargetFormat, VkImageLayout initialLayout, |
| VkImageLayout finalLayout, VkSampler sampler) { |
| 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, |
| }; |
| |
| VkPipelineViewportStateCreateInfo viewportStateCi = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, |
| .viewportCount = 1, |
| // The viewport state is dynamic. |
| .pViewports = nullptr, |
| .scissorCount = 1, |
| // The scissor state is dynamic. |
| .pScissors = nullptr, |
| }; |
| |
| 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}; |
| |
| VkDynamicState dynamicStates[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; |
| VkPipelineDynamicStateCreateInfo dynamicStateCi = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, |
| .dynamicStateCount = std::size(dynamicStates), |
| .pDynamicStates = dynamicStates, |
| }; |
| |
| VkDescriptorSetLayoutBinding layoutBindings[2] = { |
| {.binding = 0, |
| .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, |
| .descriptorCount = 1, |
| .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, |
| .pImmutableSamplers = &sampler}, |
| {.binding = 1, |
| .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| .descriptorCount = 1, |
| .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, |
| .pImmutableSamplers = nullptr}}; |
| |
| VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCi = { |
| .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .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 = &dynamicStateCi, |
| .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); |
| } |
| |
| // 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 setsPerDescriptorType = m_maxFramesInFlight * 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 = 0, |
| .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::recordCommandBuffers(uint32_t renderTargetIndex, VkCommandBuffer cmdBuffer, |
| const CompositorVkRenderTarget &renderTarget) { |
| 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 = renderTarget.m_vkFramebuffer, |
| .renderArea = {.offset = {0, 0}, .extent = {renderTarget.m_width, renderTarget.m_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); |
| VkRect2D scissor = { |
| .offset = {0, 0}, |
| .extent = |
| { |
| .width = renderTarget.m_width, |
| .height = renderTarget.m_height, |
| }, |
| }; |
| m_vk.vkCmdSetScissor(cmdBuffer, 0, 1, &scissor); |
| VkViewport viewport = { |
| .x = 0.0f, |
| .y = 0.0f, |
| .width = static_cast<float>(renderTarget.m_width), |
| .height = static_cast<float>(renderTarget.m_height), |
| .minDepth = 0.0f, |
| .maxDepth = 1.0f, |
| }; |
| m_vk.vkCmdSetViewport(cmdBuffer, 0, 1, &viewport); |
| 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 < m_currentCompositions[renderTargetIndex]->m_composeLayers.size(); |
| ++j) { |
| m_vk.vkCmdBindDescriptorSets( |
| cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_vkPipelineLayout, 0, 1, |
| &m_vkDescriptorSets[renderTargetIndex * kMaxLayersPerFrame + j], 0, nullptr); |
| m_vk.vkCmdDrawIndexed(cmdBuffer, static_cast<uint32_t>(k_indices.size()), 1, 0, 0, 0); |
| } |
| m_vk.vkCmdEndRenderPass(cmdBuffer); |
| } |
| |
| void CompositorVk::setUpUniformBuffers() { |
| VkDeviceSize size = m_uniformStorage.m_stride * m_maxFramesInFlight * 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::validateQueueFamilyProperties(const VkQueueFamilyProperties &properties) { |
| return properties.queueFlags & VK_QUEUE_GRAPHICS_BIT; |
| } |
| |
| 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]; |
| if (m_vkSampler != layer->m_vkSampler) { |
| GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) |
| << "Unsupported sampler(" << reinterpret_cast<uintptr_t>(layer->m_vkSampler) |
| << ")."; |
| } |
| imageInfos[i] = |
| VkDescriptorImageInfo({.sampler = VK_NULL_HANDLE, |
| .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); |
| } |
| |
| std::unique_ptr<CompositorVkRenderTarget> CompositorVk::createRenderTarget(VkImageView vkImageView, |
| uint32_t width, |
| uint32_t height) { |
| return std::unique_ptr<CompositorVkRenderTarget>( |
| new CompositorVkRenderTarget(m_vk, m_vkDevice, vkImageView, width, height, m_vkRenderPass)); |
| } |
| |
| 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)}}; |
| } |
| |
| CompositorVkRenderTarget::CompositorVkRenderTarget(const goldfish_vk::VulkanDispatch &vk, |
| VkDevice vkDevice, VkImageView vkImageView, |
| uint32_t width, uint32_t height, |
| VkRenderPass vkRenderPass) |
| : m_vk(vk), |
| m_vkDevice(vkDevice), |
| m_vkFramebuffer(VK_NULL_HANDLE), |
| m_width(width), |
| m_height(height) { |
| VkFramebufferCreateInfo framebufferCi = { |
| .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, |
| .flags = 0, |
| .renderPass = vkRenderPass, |
| .attachmentCount = 1, |
| .pAttachments = &vkImageView, |
| .width = width, |
| .height = height, |
| .layers = 1, |
| }; |
| VK_CHECK(m_vk.vkCreateFramebuffer(vkDevice, &framebufferCi, nullptr, &m_vkFramebuffer)); |
| } |
| |
| CompositorVkRenderTarget::~CompositorVkRenderTarget() { |
| m_vk.vkDestroyFramebuffer(m_vkDevice, m_vkFramebuffer, nullptr); |
| } |