#include "CompositorVk.h" #include #include #include #include #include "host-common/logging.h" #include "vulkan/vk_enum_string_helper.h" #include "vulkan/vk_util.h" namespace gfxstream { namespace vk { using emugl::ABORT_REASON_OTHER; using emugl::FatalError; namespace CompositorVkShader { #include "vulkan/CompositorFragmentShader.h" #include "vulkan/CompositorVertexShader.h" } // namespace CompositorVkShader namespace { constexpr const VkImageLayout kSourceImageInitialLayoutUsed = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; constexpr const VkImageLayout kSourceImageFinalLayoutUsed = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; constexpr const VkImageLayout kTargetImageInitialLayoutUsed = VK_IMAGE_LAYOUT_UNDEFINED; constexpr const VkImageLayout kTargetImageFinalLayoutUsed = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; const BorrowedImageInfoVk* getInfoOrAbort(const std::unique_ptr& info) { auto imageVk = static_cast(info.get()); if (imageVk != nullptr) { return imageVk; } GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "CompositorVk did not find BorrowedImageInfoVk"; } struct Vertex { alignas(8) glm::vec2 pos; alignas(8) glm::vec2 tex; static VkVertexInputBindingDescription getBindingDescription() { return VkVertexInputBindingDescription{ .binding = 0, .stride = sizeof(struct Vertex), .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, }; } static std::array 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, tex), }, }; } }; static const std::vector k_vertices = { // clang-format off { .pos = {-1.0f, -1.0f}, .tex = {0.0f, 0.0f}}, { .pos = { 1.0f, -1.0f}, .tex = {1.0f, 0.0f}}, { .pos = { 1.0f, 1.0f}, .tex = {1.0f, 1.0f}}, { .pos = {-1.0f, 1.0f}, .tex = {0.0f, 1.0f}}, // clang-format on }; static const std::vector k_indices = {0, 1, 2, 2, 3, 0}; static VkShaderModule createShaderModule(const VulkanDispatch& vk, VkDevice device, const std::vector& code) { const 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; } } // namespace CompositorVk::RenderTarget::RenderTarget(const VulkanDispatch& vk, VkDevice vkDevice, VkImage vkImage, VkImageView vkImageView, uint32_t width, uint32_t height, VkRenderPass vkRenderPass) : m_vk(vk), m_vkDevice(vkDevice), m_vkImage(vkImage), m_vkFramebuffer(VK_NULL_HANDLE), m_width(width), m_height(height) { if (vkImageView == VK_NULL_HANDLE) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "CompositorVk found empty image view handle when creating RenderTarget."; } const 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)); } CompositorVk::RenderTarget::~RenderTarget() { if (m_vkFramebuffer != VK_NULL_HANDLE) { m_vk.vkDestroyFramebuffer(m_vkDevice, m_vkFramebuffer, nullptr); } } std::unique_ptr CompositorVk::create( const VulkanDispatch& vk, VkDevice vkDevice, VkPhysicalDevice vkPhysicalDevice, VkQueue vkQueue, std::shared_ptr queueLock, uint32_t queueFamilyIndex, uint32_t maxFramesInFlight, DebugUtilsHelper debugUtils) { auto res = std::unique_ptr(new CompositorVk(vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, queueFamilyIndex, maxFramesInFlight, debugUtils)); res->setUpCommandPool(); res->setUpSampler(); res->setUpGraphicsPipeline(); res->setUpVertexBuffers(); res->setUpUniformBuffers(); res->setUpDescriptorSets(); res->setUpFences(); res->setUpDefaultImage(); res->setUpFrameResourceFutures(); return res; } CompositorVk::CompositorVk(const VulkanDispatch& vk, VkDevice vkDevice, VkPhysicalDevice vkPhysicalDevice, VkQueue vkQueue, std::shared_ptr queueLock, uint32_t queueFamilyIndex, uint32_t maxFramesInFlight, DebugUtilsHelper debugUtilsHelper) : CompositorVkBase(vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, queueFamilyIndex, maxFramesInFlight, debugUtilsHelper), m_maxFramesInFlight(maxFramesInFlight), m_renderTargetCache(k_renderTargetCacheSize) {} CompositorVk::~CompositorVk() { { android::base::AutoLock lock(*m_vkQueueLock); VK_CHECK(vk_util::waitForVkQueueIdleWithRetry(m_vk, m_vkQueue)); } if (m_defaultImage.m_vkImageView != VK_NULL_HANDLE) { m_vk.vkDestroyImageView(m_vkDevice, m_defaultImage.m_vkImageView, nullptr); } if (m_defaultImage.m_vkImage != VK_NULL_HANDLE) { m_vk.vkDestroyImage(m_vkDevice, m_defaultImage.m_vkImage, nullptr); } if (m_defaultImage.m_vkImageMemory != VK_NULL_HANDLE) { m_vk.vkFreeMemory(m_vkDevice, m_defaultImage.m_vkImageMemory, nullptr); } 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); for (auto& [_, formatResources] : m_formatResources) { m_vk.vkDestroyPipeline(m_vkDevice, formatResources.m_graphicsVkPipeline, nullptr); m_vk.vkDestroyRenderPass(m_vkDevice, formatResources.m_vkRenderPass, nullptr); } m_vk.vkDestroyPipelineLayout(m_vkDevice, m_vkPipelineLayout, nullptr); m_vk.vkDestroySampler(m_vkDevice, m_vkSampler, nullptr); m_vk.vkDestroyDescriptorSetLayout(m_vkDevice, m_vkDescriptorSetLayout, nullptr); m_vk.vkDestroyCommandPool(m_vkDevice, m_vkCommandPool, nullptr); for (PerFrameResources& frameResources : m_frameResources) { m_vk.vkDestroyFence(m_vkDevice, frameResources.m_vkFence, nullptr); } } void CompositorVk::setUpGraphicsPipeline() { 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); const VkPipelineShaderStageCreateInfo shaderStageCis[2] = { VkPipelineShaderStageCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = vertShaderMod, .pName = "main", }, VkPipelineShaderStageCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = fragShaderMod, .pName = "main", }, }; const auto vertexAttributeDescription = Vertex::getAttributeDescription(); const auto vertexBindingDescription = Vertex::getBindingDescription(); const VkPipelineVertexInputStateCreateInfo vertexInputStateCi = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .vertexBindingDescriptionCount = 1, .pVertexBindingDescriptions = &vertexBindingDescription, .vertexAttributeDescriptionCount = static_cast(vertexAttributeDescription.size()), .pVertexAttributeDescriptions = vertexAttributeDescription.data(), }; const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCi = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .primitiveRestartEnable = VK_FALSE, }; const 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, }; const 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, }; const 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, }; const 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, }; const VkPipelineColorBlendStateCreateInfo colorBlendStateCi = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .logicOpEnable = VK_FALSE, .attachmentCount = 1, .pAttachments = &colorBlendAttachment, }; const VkDynamicState dynamicStates[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, }; const VkPipelineDynamicStateCreateInfo dynamicStateCi = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .dynamicStateCount = std::size(dynamicStates), .pDynamicStates = dynamicStates, }; const VkDescriptorSetLayoutBinding layoutBindings[2] = { VkDescriptorSetLayoutBinding{ .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = &m_vkSampler, }, VkDescriptorSetLayoutBinding{ .binding = 1, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = nullptr, }, }; const VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCi = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .pNext = nullptr, .flags = 0, .bindingCount = static_cast(std::size(layoutBindings)), .pBindings = layoutBindings, }; VK_CHECK(m_vk.vkCreateDescriptorSetLayout(m_vkDevice, &descriptorSetLayoutCi, nullptr, &m_vkDescriptorSetLayout)); const 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 = VK_FORMAT_UNDEFINED, .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 = kTargetImageInitialLayoutUsed, .finalLayout = kTargetImageFinalLayoutUsed, }; const VkAttachmentReference colorAttachmentRef = { .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }; const 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. const 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, }; const VkRenderPassCreateInfo renderPassCi = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .attachmentCount = 1, .pAttachments = &colorAttachment, .subpassCount = 1, .pSubpasses = &subpass, .dependencyCount = 1, .pDependencies = &subpassDependency, }; 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 = &dynamicStateCi, .layout = m_vkPipelineLayout, .renderPass = VK_NULL_HANDLE, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1, }; const std::vector kRenderTargetFormats = { VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM, }; for (VkFormat renderTargetFormat : kRenderTargetFormats) { colorAttachment.format = renderTargetFormat; VkRenderPass renderPass = VK_NULL_HANDLE; VK_CHECK(m_vk.vkCreateRenderPass(m_vkDevice, &renderPassCi, nullptr, &renderPass)); graphicsPipelineCi.renderPass = renderPass; VkPipeline pipeline = VK_NULL_HANDLE; VK_CHECK(m_vk.vkCreateGraphicsPipelines(m_vkDevice, VK_NULL_HANDLE, 1, &graphicsPipelineCi, nullptr, &pipeline)); m_formatResources[renderTargetFormat] = PerFormatResources{ .m_vkRenderPass = renderPass, .m_graphicsVkPipeline = pipeline, }; } m_vk.vkDestroyShaderModule(m_vkDevice, vertShaderMod, nullptr); m_vk.vkDestroyShaderModule(m_vkDevice, fragShaderMod, nullptr); } void CompositorVk::setUpVertexBuffers() { const VkDeviceSize vertexBufferSize = sizeof(Vertex) * 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::setUpDescriptorSets() { const uint32_t descriptorSetsPerFrame = kMaxLayersPerFrame; const uint32_t descriptorSetsTotal = descriptorSetsPerFrame * m_maxFramesInFlight; const uint32_t descriptorsOfEachTypePerSet = 1; const uint32_t descriptorsOfEachTypePerFrame = descriptorSetsPerFrame * descriptorsOfEachTypePerSet; const uint32_t descriptorsOfEachTypeTotal = descriptorsOfEachTypePerFrame * m_maxFramesInFlight; const VkDescriptorPoolSize descriptorPoolSizes[2] = { VkDescriptorPoolSize{ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = descriptorsOfEachTypeTotal, }, VkDescriptorPoolSize{ .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = descriptorsOfEachTypeTotal, }}; const VkDescriptorPoolCreateInfo descriptorPoolCi = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .flags = 0, .maxSets = descriptorSetsTotal, .poolSizeCount = static_cast(std::size(descriptorPoolSizes)), .pPoolSizes = descriptorPoolSizes, }; VK_CHECK( m_vk.vkCreateDescriptorPool(m_vkDevice, &descriptorPoolCi, nullptr, &m_vkDescriptorPool)); const std::vector frameDescriptorSetLayouts(descriptorSetsPerFrame, m_vkDescriptorSetLayout); const VkDescriptorSetAllocateInfo frameDescriptorSetAllocInfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .descriptorPool = m_vkDescriptorPool, .descriptorSetCount = descriptorSetsPerFrame, .pSetLayouts = frameDescriptorSetLayouts.data(), }; VkDeviceSize uniformBufferOffset = 0; for (uint32_t frameIndex = 0; frameIndex < m_maxFramesInFlight; ++frameIndex) { PerFrameResources& frameResources = m_frameResources[frameIndex]; frameResources.m_layerDescriptorSets.resize(descriptorSetsPerFrame); VK_CHECK(m_vk.vkAllocateDescriptorSets(m_vkDevice, &frameDescriptorSetAllocInfo, frameResources.m_layerDescriptorSets.data())); for (uint32_t layerIndex = 0; layerIndex < kMaxLayersPerFrame; ++layerIndex) { const VkDescriptorBufferInfo bufferInfo = { .buffer = m_uniformStorage.m_vkBuffer, .offset = uniformBufferOffset, .range = sizeof(UniformBufferBinding), }; const VkWriteDescriptorSet descriptorSetWrite = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = frameResources.m_layerDescriptorSets[layerIndex], .dstBinding = 1, .dstArrayElement = 0, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .pBufferInfo = &bufferInfo, }; m_vk.vkUpdateDescriptorSets(m_vkDevice, 1, &descriptorSetWrite, 0, nullptr); uniformBufferOffset += m_uniformStorage.m_stride; } } } void CompositorVk::setUpCommandPool() { const VkCommandPoolCreateInfo commandPoolCreateInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .flags = 0, .queueFamilyIndex = m_queueFamilyIndex, }; VkCommandPool commandPool = VK_NULL_HANDLE; VK_CHECK(m_vk.vkCreateCommandPool(m_vkDevice, &commandPoolCreateInfo, nullptr, &commandPool)); m_vkCommandPool = commandPool; m_debugUtilsHelper.addDebugLabel(m_vkCommandPool, "CompositorVk command pool"); } void CompositorVk::setUpFences() { for (uint32_t frameIndex = 0; frameIndex < m_maxFramesInFlight; ++frameIndex) { PerFrameResources& frameResources = m_frameResources[frameIndex]; const VkFenceCreateInfo fenceCi = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, }; VkFence fence; VK_CHECK(m_vk.vkCreateFence(m_vkDevice, &fenceCi, nullptr, &fence)); frameResources.m_vkFence = fence; } } void CompositorVk::setUpDefaultImage() { const VkImageCreateInfo imageCreateInfo = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .imageType = VK_IMAGE_TYPE_2D, .format = VK_FORMAT_R8G8B8A8_UNORM, .extent = { .width = 2, .height = 2, .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, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, }; VkImage image = VK_NULL_HANDLE; VK_CHECK(m_vk.vkCreateImage(m_vkDevice, &imageCreateInfo, nullptr, &image)); VkMemoryRequirements imageMemoryRequirements; m_vk.vkGetImageMemoryRequirements(m_vkDevice, image, &imageMemoryRequirements); auto memoryTypeIndexOpt = findMemoryType(imageMemoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); if (!memoryTypeIndexOpt) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "CompositorVk failed to find memory type for default image."; } const VkMemoryAllocateInfo imageMemoryAllocInfo = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = nullptr, .allocationSize = imageMemoryRequirements.size, .memoryTypeIndex = *memoryTypeIndexOpt, }; VkDeviceMemory imageMemory = VK_NULL_HANDLE; VK_CHECK_MEMALLOC( m_vk.vkAllocateMemory(m_vkDevice, &imageMemoryAllocInfo, nullptr, &imageMemory), imageMemoryAllocInfo); VK_CHECK(m_vk.vkBindImageMemory(m_vkDevice, image, imageMemory, 0)); const VkImageViewCreateInfo imageViewCreateInfo = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .pNext = nullptr, .flags = 0, .image = image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = VK_FORMAT_R8G8B8A8_UNORM, .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, }, }; VkImageView imageView = VK_NULL_HANDLE; VK_CHECK(m_vk.vkCreateImageView(m_vkDevice, &imageViewCreateInfo, nullptr, &imageView)); const std::vector pixels = { 0xFF, 0x00, 0xFF, 0xFF, // 0xFF, 0x00, 0xFF, 0xFF, // 0xFF, 0x00, 0xFF, 0xFF, // 0xFF, 0x00, 0xFF, 0xFF, // }; VkBuffer stagingBuffer = VK_NULL_HANDLE; VkDeviceMemory stagingBufferMemory = VK_NULL_HANDLE; std::tie(stagingBuffer, stagingBufferMemory) = createStagingBufferWithData(pixels.data(), pixels.size()); runSingleTimeCommands(m_vkQueue, m_vkQueueLock, [&, this](const VkCommandBuffer& cmdBuff) { const VkImageMemoryBarrier toTransferDstImageBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = 0, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = image, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }, }; m_vk.vkCmdPipelineBarrier(cmdBuff, /*srcStageMask=*/VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, /*dstStageMask=*/VK_PIPELINE_STAGE_TRANSFER_BIT, /*dependencyFlags=*/0, /*memoryBarrierCount=*/0, /*pMemoryBarriers=*/nullptr, /*bufferMemoryBarrierCount=*/0, /*pBufferMemoryBarriers=*/nullptr, 1, &toTransferDstImageBarrier); const VkBufferImageCopy bufferToImageCopy = { .bufferOffset = 0, .bufferRowLength = 0, .bufferImageHeight = 0, .imageSubresource = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1, }, .imageOffset = { .x = 0, .y = 0, .z = 0, }, .imageExtent = { .width = 2, .height = 2, .depth = 1, }, }; m_vk.vkCmdCopyBufferToImage(cmdBuff, stagingBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferToImageCopy); const VkImageMemoryBarrier toSampledImageImageBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = image, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }, }; m_vk.vkCmdPipelineBarrier(cmdBuff, /*srcStageMask=*/VK_PIPELINE_STAGE_TRANSFER_BIT, /*dstStageMask=*/VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, /*dependencyFlags=*/0, /*memoryBarrierCount=*/0, /*pMemoryBarriers=*/nullptr, /*bufferMemoryBarrierCount=*/0, /*pBufferMemoryBarriers=*/nullptr, 1, &toSampledImageImageBarrier); }); m_vk.vkDestroyBuffer(m_vkDevice, stagingBuffer, nullptr); m_vk.vkFreeMemory(m_vkDevice, stagingBufferMemory, nullptr); m_defaultImage.m_vkImage = image; m_defaultImage.m_vkImageView = imageView; m_defaultImage.m_vkImageMemory = imageMemory; } void CompositorVk::setUpFrameResourceFutures() { for (uint32_t frameIndex = 0; frameIndex < m_maxFramesInFlight; ++frameIndex) { std::shared_future availableFrameResourceFuture = std::async(std::launch::deferred, [this, frameIndex] { return &m_frameResources[frameIndex]; }).share(); m_availableFrameResources.push_back(std::move(availableFrameResourceFuture)); } } void CompositorVk::setUpUniformBuffers() { VkPhysicalDeviceProperties physicalDeviceProperties; m_vk.vkGetPhysicalDeviceProperties(m_vkPhysicalDevice, &physicalDeviceProperties); const VkDeviceSize alignment = physicalDeviceProperties.limits.minUniformBufferOffsetAlignment; m_uniformStorage.m_stride = ((sizeof(UniformBufferBinding) - 1) / alignment + 1) * alignment; 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(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; void* mapped = nullptr; VK_CHECK(m_vk.vkMapMemory(m_vkDevice, m_uniformStorage.m_vkDeviceMemory, 0, size, 0, &mapped)); uint8_t* data = reinterpret_cast(mapped); for (uint32_t frameIndex = 0; frameIndex < m_maxFramesInFlight; ++frameIndex) { PerFrameResources& frameResources = m_frameResources[frameIndex]; for (uint32_t layerIndex = 0; layerIndex < kMaxLayersPerFrame; ++layerIndex) { auto* layerUboStorage = reinterpret_cast(data); frameResources.m_layerUboStorages.push_back(layerUboStorage); data += m_uniformStorage.m_stride; } } } void CompositorVk::setUpSampler() { // The texture coordinate transformation matrices for flip/rotate/etc // currently depends on this being repeat. constexpr const VkSamplerAddressMode kSamplerMode = VK_SAMPLER_ADDRESS_MODE_REPEAT; const VkSamplerCreateInfo samplerCi = { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .magFilter = VK_FILTER_LINEAR, .minFilter = VK_FILTER_LINEAR, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .addressModeU = kSamplerMode, .addressModeV = kSamplerMode, .addressModeW = kSamplerMode, .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_vkSampler)); } // 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 { 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()) { ERR("Failed to find memory type for creating buffer."); m_vk.vkDestroyBuffer(m_vkDevice, resBuffer, nullptr); return std::nullopt; } const VkMemoryAllocateInfo memAllocInfo = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = memRequirements.size, .memoryTypeIndex = maybeMemoryTypeIndex.value(), }; VkDeviceMemory resMemory; VK_CHECK_MEMALLOC(m_vk.vkAllocateMemory(m_vkDevice, &memAllocInfo, nullptr, &resMemory), memAllocInfo); 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, 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); }); } // TODO: move this to another common CRTP helper class in vk_util.h. VkFormatFeatureFlags CompositorVk::getFormatFeatures(VkFormat format, VkImageTiling tiling) { auto i = m_vkFormatProperties.find(format); if (i == m_vkFormatProperties.end()) { VkFormatProperties formatProperties; m_vk.vkGetPhysicalDeviceFormatProperties(m_vkPhysicalDevice, format, &formatProperties); i = m_vkFormatProperties.emplace(format, formatProperties).first; } const VkFormatProperties& formatProperties = i->second; VkFormatFeatureFlags formatFeatures = 0; if (tiling == VK_IMAGE_TILING_LINEAR) { formatFeatures = formatProperties.linearTilingFeatures; } else if (tiling == VK_IMAGE_TILING_OPTIMAL) { formatFeatures = formatProperties.optimalTilingFeatures; } else { ERR("Unknown tiling:%#" PRIx64 ".", static_cast(tiling)); } return formatFeatures; } CompositorVk::RenderTarget* CompositorVk::getOrCreateRenderTargetInfo( const BorrowedImageInfoVk& imageInfo) { auto* renderTargetPtr = m_renderTargetCache.get(imageInfo.id); if (renderTargetPtr != nullptr) { return renderTargetPtr->get(); } auto formatResourcesIt = m_formatResources.find(imageInfo.imageCreateInfo.format); if (formatResourcesIt == m_formatResources.end()) { return nullptr; } auto& formatResources = formatResourcesIt->second; auto* renderTarget = new RenderTarget(m_vk, m_vkDevice, imageInfo.image, imageInfo.imageView, imageInfo.imageCreateInfo.extent.width, imageInfo.imageCreateInfo.extent.height, formatResources.m_vkRenderPass); m_renderTargetCache.set(imageInfo.id, std::unique_ptr(renderTarget)); return renderTarget; } bool CompositorVk::canCompositeFrom(const VkImageCreateInfo& imageCi) { VkFormatFeatureFlags formatFeatures = getFormatFeatures(imageCi.format, imageCi.tiling); if (!(formatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { ERR("The format, %s, with tiling, %s, doesn't support the " "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT feature. All supported features are %s.", string_VkFormat(imageCi.format), string_VkImageTiling(imageCi.tiling), string_VkFormatFeatureFlags(formatFeatures).c_str()); return false; } return true; } bool CompositorVk::canCompositeTo(const VkImageCreateInfo& imageCi) { VkFormatFeatureFlags formatFeatures = getFormatFeatures(imageCi.format, imageCi.tiling); if (!(formatFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { ERR("The format, %s, with tiling, %s, doesn't support the " "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT feature. All supported features are %s.", string_VkFormat(imageCi.format), string_VkImageTiling(imageCi.tiling), string_VkFormatFeatureFlags(formatFeatures).c_str()); return false; } if (!(imageCi.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) { ERR("The VkImage is not created with the VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT usage flag. " "The usage flags are %s.", string_VkImageUsageFlags(imageCi.usage).c_str()); return false; } if (m_formatResources.find(imageCi.format) == m_formatResources.end()) { ERR("The format of the image, %s, is not supported by the CompositorVk as the render " "target.", string_VkFormat(imageCi.format)); return false; } return true; } void CompositorVk::buildCompositionVk(const CompositionRequest& compositionRequest, CompositionVk* compositionVk) { const BorrowedImageInfoVk* targetImage = getInfoOrAbort(compositionRequest.target); auto formatResourcesIt = m_formatResources.find(targetImage->imageCreateInfo.format); if (formatResourcesIt == m_formatResources.end()) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "CompositorVk did not find format resource for format " << targetImage->imageCreateInfo.format; } const auto& formatResources = formatResourcesIt->second; RenderTarget* targetImageRenderTarget = getOrCreateRenderTargetInfo(*targetImage); const uint32_t targetWidth = targetImage->width; const uint32_t targetHeight = targetImage->height; compositionVk->targetImage = targetImage; compositionVk->targetRenderPass = formatResources.m_vkRenderPass; compositionVk->targetFramebuffer = targetImageRenderTarget->m_vkFramebuffer; compositionVk->pipeline = formatResources.m_graphicsVkPipeline; for (const CompositionRequestLayer& layer : compositionRequest.layers) { uint32_t sourceImageWidth = 0; uint32_t sourceImageHeight = 0; const BorrowedImageInfoVk* sourceImage = nullptr; if (layer.props.composeMode == HWC2_COMPOSITION_SOLID_COLOR) { sourceImageWidth = targetWidth; sourceImageHeight = targetHeight; } else { sourceImage = getInfoOrAbort(layer.source); if (!canCompositeFrom(sourceImage->imageCreateInfo)) { continue; } sourceImageWidth = sourceImage->width; sourceImageHeight = sourceImage->height; } // 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 layer. // 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 = layer.props.displayFrame; const hwc_frect_t& texcoordRect = layer.props.crop; const int posWidth = posRect.right - posRect.left; const int posHeight = posRect.bottom - posRect.top; const float posScaleX = float(posWidth) / targetWidth; const float posScaleY = float(posHeight) / targetHeight; const float posTranslateX = -1.0f + posScaleX + 2.0f * float(posRect.left) / targetWidth; const float posTranslateY = -1.0f + posScaleY + 2.0f * float(posRect.top) / targetHeight; float texCoordScaleX = (texcoordRect.right - texcoordRect.left) / float(sourceImageWidth); float texCoordScaleY = (texcoordRect.bottom - texcoordRect.top) / float(sourceImageHeight); const float texCoordTranslateX = texcoordRect.left / float(sourceImageWidth); const float texCoordTranslateY = texcoordRect.top / float(sourceImageHeight); float texcoordRotation = 0.0f; const float pi = glm::pi(); switch (layer.props.transform) { case HWC_TRANSFORM_NONE: break; 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: texCoordScaleX *= -1.0f; break; case HWC_TRANSFORM_FLIP_V: texCoordScaleY *= -1.0f; break; case HWC_TRANSFORM_FLIP_H_ROT_90: texcoordRotation = pi * 0.5f; texCoordScaleX *= -1.0f; break; case HWC_TRANSFORM_FLIP_V_ROT_90: texcoordRotation = pi * 0.5f; texCoordScaleY *= -1.0f; break; default: ERR("Unknown transform:%d", static_cast(layer.props.transform)); break; } DescriptorSetContents descriptorSetContents = { .binding1 = { .positionTransform = glm::translate(glm::mat4(1.0f), glm::vec3(posTranslateX, posTranslateY, 0.0f)) * glm::scale(glm::mat4(1.0f), glm::vec3(posScaleX, posScaleY, 1.0f)), .texCoordTransform = glm::translate(glm::mat4(1.0f), glm::vec3(texCoordTranslateX, texCoordTranslateY, 0.0f)) * glm::scale(glm::mat4(1.0f), glm::vec3(texCoordScaleX, texCoordScaleY, 1.0f)) * glm::rotate(glm::mat4(1.0f), texcoordRotation, glm::vec3(0.0f, 0.0f, 1.0f)), .mode = glm::uvec4(static_cast(layer.props.composeMode), 0, 0, 0), .alpha = glm::vec4(layer.props.alpha, layer.props.alpha, layer.props.alpha, layer.props.alpha), }, }; if (layer.props.composeMode == HWC2_COMPOSITION_SOLID_COLOR) { descriptorSetContents.binding0.sampledImageId = 0; descriptorSetContents.binding0.sampledImageView = m_defaultImage.m_vkImageView; descriptorSetContents.binding1.color = glm::vec4(static_cast(layer.props.color.r) / 255.0f, static_cast(layer.props.color.g) / 255.0f, static_cast(layer.props.color.b) / 255.0f, static_cast(layer.props.color.a) / 255.0f); } else { if (sourceImage == nullptr) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "CompositorVk failed to find sourceImage."; } descriptorSetContents.binding0.sampledImageId = sourceImage->id; descriptorSetContents.binding0.sampledImageView = sourceImage->imageView; compositionVk->layersSourceImages.emplace_back(sourceImage); } compositionVk->layersDescriptorSets.descriptorSets.emplace_back(descriptorSetContents); } } CompositorVk::CompositionFinishedWaitable CompositorVk::compose( const CompositionRequest& compositionRequest) { CompositionVk compositionVk; buildCompositionVk(compositionRequest, &compositionVk); // Grab and wait for the next available resources. if (m_availableFrameResources.empty()) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "CompositorVk failed to get PerFrameResources."; } auto frameResourceFuture = std::move(m_availableFrameResources.front()); m_availableFrameResources.pop_front(); PerFrameResources* frameResources = frameResourceFuture.get(); updateDescriptorSetsIfChanged(compositionVk.layersDescriptorSets, frameResources); std::vector preCompositionQueueTransferBarriers; std::vector preCompositionLayoutTransitionBarriers; std::vector postCompositionLayoutTransitionBarriers; std::vector postCompositionQueueTransferBarriers; addNeededBarriersToUseBorrowedImage( *compositionVk.targetImage, m_queueFamilyIndex, kTargetImageInitialLayoutUsed, kTargetImageFinalLayoutUsed, VK_ACCESS_MEMORY_WRITE_BIT, &preCompositionQueueTransferBarriers, &preCompositionLayoutTransitionBarriers, &postCompositionLayoutTransitionBarriers, &postCompositionQueueTransferBarriers); for (const BorrowedImageInfoVk* sourceImage : compositionVk.layersSourceImages) { addNeededBarriersToUseBorrowedImage( *sourceImage, m_queueFamilyIndex, kSourceImageInitialLayoutUsed, kSourceImageFinalLayoutUsed, VK_ACCESS_SHADER_READ_BIT, &preCompositionQueueTransferBarriers, &preCompositionLayoutTransitionBarriers, &postCompositionLayoutTransitionBarriers, &postCompositionQueueTransferBarriers); } static uint32_t sCompositionNumber = 0; const uint32_t thisCompositionNumber = sCompositionNumber++; VkCommandBuffer& commandBuffer = frameResources->m_vkCommandBuffer; if (commandBuffer != VK_NULL_HANDLE) { m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool, 1, &commandBuffer); } const VkCommandBufferAllocateInfo commandBufferAllocInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = m_vkCommandPool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; VK_CHECK(m_vk.vkAllocateCommandBuffers(m_vkDevice, &commandBufferAllocInfo, &commandBuffer)); m_debugUtilsHelper.addDebugLabel(commandBuffer, "CompositorVk composition:%d command buffer", thisCompositionNumber); const VkCommandBufferBeginInfo beginInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; VK_CHECK(m_vk.vkBeginCommandBuffer(commandBuffer, &beginInfo)); m_debugUtilsHelper.cmdBeginDebugLabel(commandBuffer, "CompositorVk composition:%d into ColorBuffer:%d", thisCompositionNumber, compositionVk.targetImage->id); if (!preCompositionQueueTransferBarriers.empty()) { m_vk.vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, static_cast(preCompositionQueueTransferBarriers.size()), preCompositionQueueTransferBarriers.data()); } if (!preCompositionLayoutTransitionBarriers.empty()) { m_vk.vkCmdPipelineBarrier( commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, static_cast(preCompositionLayoutTransitionBarriers.size()), preCompositionLayoutTransitionBarriers.data()); } const VkClearValue renderTargetClearColor = { .color = { .float32 = {0.0f, 0.0f, 0.0f, 1.0f}, }, }; const VkRenderPassBeginInfo renderPassBeginInfo = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderPass = compositionVk.targetRenderPass, .framebuffer = compositionVk.targetFramebuffer, .renderArea = { .offset = { .x = 0, .y = 0, }, .extent = { .width = compositionVk.targetImage->imageCreateInfo.extent.width, .height = compositionVk.targetImage->imageCreateInfo.extent.height, }, }, .clearValueCount = 1, .pClearValues = &renderTargetClearColor, }; m_vk.vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); m_vk.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, compositionVk.pipeline); const VkRect2D scissor = { .offset = { .x = 0, .y = 0, }, .extent = { .width = compositionVk.targetImage->imageCreateInfo.extent.width, .height = compositionVk.targetImage->imageCreateInfo.extent.height, }, }; m_vk.vkCmdSetScissor(commandBuffer, 0, 1, &scissor); const VkViewport viewport = { .x = 0.0f, .y = 0.0f, .width = static_cast(compositionVk.targetImage->imageCreateInfo.extent.width), .height = static_cast(compositionVk.targetImage->imageCreateInfo.extent.height), .minDepth = 0.0f, .maxDepth = 1.0f, }; m_vk.vkCmdSetViewport(commandBuffer, 0, 1, &viewport); const VkDeviceSize offsets[] = {0}; m_vk.vkCmdBindVertexBuffers(commandBuffer, 0, 1, &m_vertexVkBuffer, offsets); m_vk.vkCmdBindIndexBuffer(commandBuffer, m_indexVkBuffer, 0, VK_INDEX_TYPE_UINT16); const uint32_t numLayers = compositionVk.layersDescriptorSets.descriptorSets.size(); for (uint32_t layerIndex = 0; layerIndex < numLayers; ++layerIndex) { m_debugUtilsHelper.cmdBeginDebugLabel(commandBuffer, "CompositorVk compose layer:%d", layerIndex); VkDescriptorSet layerDescriptorSet = frameResources->m_layerDescriptorSets[layerIndex]; m_vk.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_vkPipelineLayout, /*firstSet=*/0, /*descriptorSetCount=*/1, &layerDescriptorSet, /*dynamicOffsetCount=*/0, /*pDynamicOffsets=*/nullptr); m_vk.vkCmdDrawIndexed(commandBuffer, static_cast(k_indices.size()), 1, 0, 0, 0); m_debugUtilsHelper.cmdEndDebugLabel(commandBuffer); } m_vk.vkCmdEndRenderPass(commandBuffer); // Insert a VkImageMemoryBarrier so that the vkCmdBlitImage in post will wait for the rendering // to the render target to complete. const VkImageMemoryBarrier renderTargetBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = compositionVk.targetImage->image, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }, }; m_vk.vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, /*dependencyFlags=*/0, /*memoryBarrierCount=*/0, /*pMemoryBarriers=*/nullptr, /*bufferMemoryBarrierCount=*/0, /*pBufferMemoryBarriers=*/nullptr, 1, &renderTargetBarrier); if (!postCompositionLayoutTransitionBarriers.empty()) { m_vk.vkCmdPipelineBarrier( commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, static_cast(postCompositionLayoutTransitionBarriers.size()), postCompositionLayoutTransitionBarriers.data()); } if (!postCompositionQueueTransferBarriers.empty()) { m_vk.vkCmdPipelineBarrier( commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, static_cast(postCompositionQueueTransferBarriers.size()), postCompositionQueueTransferBarriers.data()); } m_debugUtilsHelper.cmdEndDebugLabel(commandBuffer); VK_CHECK(m_vk.vkEndCommandBuffer(commandBuffer)); VkFence composeCompleteFence = frameResources->m_vkFence; m_debugUtilsHelper.addDebugLabel( composeCompleteFence, "CompositorVk composition:%d complete fence", thisCompositionNumber); VK_CHECK(m_vk.vkResetFences(m_vkDevice, 1, &composeCompleteFence)); const VkPipelineStageFlags submitWaitStages[] = { VK_PIPELINE_STAGE_TRANSFER_BIT, }; const VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 0, .pWaitSemaphores = nullptr, .pWaitDstStageMask = submitWaitStages, .commandBufferCount = 1, .pCommandBuffers = &commandBuffer, .signalSemaphoreCount = 0, .pSignalSemaphores = nullptr, }; { android::base::AutoLock lock(*m_vkQueueLock); VK_CHECK(m_vk.vkQueueSubmit(m_vkQueue, 1, &submitInfo, composeCompleteFence)); } // Create a future that will return the PerFrameResources to the next // iteration of CompostiorVk::compose() once this current composition // completes. std::shared_future composeCompleteFutureForResources = std::async(std::launch::deferred, [composeCompleteFence, frameResources, this]() mutable { VkResult res = m_vk.vkWaitForFences(m_vkDevice, 1, &composeCompleteFence, VK_TRUE, kVkWaitForFencesTimeoutNsecs); if (res == VK_SUCCESS) { return frameResources; } if (res == VK_TIMEOUT) { // Retry. If device lost, hopefully this returns immediately. res = m_vk.vkWaitForFences(m_vkDevice, 1, &composeCompleteFence, VK_TRUE, kVkWaitForFencesTimeoutNsecs); } VK_CHECK(res); return frameResources; }).share(); m_availableFrameResources.push_back(composeCompleteFutureForResources); // Create a future that will return once this current composition // completes that can be shared outside of CompositorVk. std::shared_future composeCompleteFuture = std::async(std::launch::deferred, [composeCompleteFutureForResources]() { composeCompleteFutureForResources.get(); }).share(); return composeCompleteFuture; } void CompositorVk::onImageDestroyed(uint32_t imageId) { m_renderTargetCache.remove(imageId); } bool operator==(const CompositorVkBase::DescriptorSetContents& lhs, const CompositorVkBase::DescriptorSetContents& rhs) { return std::tie(lhs.binding0.sampledImageId, // lhs.binding0.sampledImageView, // lhs.binding1.mode, // lhs.binding1.alpha, // lhs.binding1.color, // lhs.binding1.positionTransform, // lhs.binding1.texCoordTransform) // == // std::tie(rhs.binding0.sampledImageId, // rhs.binding0.sampledImageView, // rhs.binding1.mode, // rhs.binding1.alpha, // rhs.binding1.color, // rhs.binding1.positionTransform, // rhs.binding1.texCoordTransform); } bool operator==(const CompositorVkBase::FrameDescriptorSetsContents& lhs, const CompositorVkBase::FrameDescriptorSetsContents& rhs) { return lhs.descriptorSets == rhs.descriptorSets; } void CompositorVk::updateDescriptorSetsIfChanged( const FrameDescriptorSetsContents& descriptorSetsContents, PerFrameResources* frameResources) { if (frameResources->m_vkDescriptorSetsContents == descriptorSetsContents) { return; } const uint32_t numRequestedLayers = static_cast(descriptorSetsContents.descriptorSets.size()); if (numRequestedLayers > kMaxLayersPerFrame) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "CompositorVk can't compose more than " << kMaxLayersPerFrame << " layers. layers asked: " << numRequestedLayers; return; } std::vector descriptorImageInfos(numRequestedLayers); std::vector descriptorWrites; for (uint32_t layerIndex = 0; layerIndex < numRequestedLayers; ++layerIndex) { const DescriptorSetContents& layerDescriptorSetContents = descriptorSetsContents.descriptorSets[layerIndex]; descriptorImageInfos[layerIndex] = VkDescriptorImageInfo{ // Empty as we only use immutable samplers. .sampler = VK_NULL_HANDLE, .imageView = layerDescriptorSetContents.binding0.sampledImageView, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; descriptorWrites.emplace_back(VkWriteDescriptorSet{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = frameResources->m_layerDescriptorSets[layerIndex], .dstBinding = 0, .dstArrayElement = 0, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .pImageInfo = &descriptorImageInfos[layerIndex], }); UniformBufferBinding* layerUboStorage = frameResources->m_layerUboStorages[layerIndex]; *layerUboStorage = layerDescriptorSetContents.binding1; } m_vk.vkUpdateDescriptorSets(m_vkDevice, descriptorWrites.size(), descriptorWrites.data(), 0, nullptr); frameResources->m_vkDescriptorSetsContents = descriptorSetsContents; } } // namespace vk } // namespace gfxstream