| #include "DisplayVk.h" |
| |
| #include <algorithm> |
| #include <chrono> |
| #include <glm/glm.hpp> |
| #include <glm/gtx/matrix_transform_2d.hpp> |
| |
| #include "host-common/GfxstreamFatalError.h" |
| #include "host-common/logging.h" |
| #include "vulkan/VkCommonOperations.h" |
| #include "vulkan/VkFormatUtils.h" |
| #include "vulkan/vk_enum_string_helper.h" |
| |
| using emugl::ABORT_REASON_OTHER; |
| using emugl::FatalError; |
| |
| #define DISPLAY_VK_ERROR(fmt, ...) \ |
| do { \ |
| fprintf(stderr, "%s(%s:%d): " fmt "\n", __func__, __FILE__, __LINE__, ##__VA_ARGS__); \ |
| fflush(stderr); \ |
| } while (0) |
| |
| #define DISPLAY_VK_ERROR_ONCE(fmt, ...) \ |
| do { \ |
| static bool displayVkInternalLogged = false; \ |
| if (!displayVkInternalLogged) { \ |
| DISPLAY_VK_ERROR(fmt, ##__VA_ARGS__); \ |
| displayVkInternalLogged = true; \ |
| } \ |
| } while (0) |
| |
| namespace { |
| |
| bool shouldRecreateSwapchain(VkResult result) { |
| return result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR; |
| } |
| |
| } // namespace |
| |
| DisplayVk::DisplayVk(const goldfish_vk::VulkanDispatch &vk, VkPhysicalDevice vkPhysicalDevice, |
| uint32_t swapChainQueueFamilyIndex, uint32_t compositorQueueFamilyIndex, |
| VkDevice vkDevice, VkQueue compositorVkQueue, |
| std::shared_ptr<android::base::Lock> compositorVkQueueLock, |
| VkQueue swapChainVkqueue, |
| std::shared_ptr<android::base::Lock> swapChainVkQueueLock) |
| : m_vk(vk), |
| m_vkPhysicalDevice(vkPhysicalDevice), |
| m_swapChainQueueFamilyIndex(swapChainQueueFamilyIndex), |
| m_compositorQueueFamilyIndex(compositorQueueFamilyIndex), |
| m_vkDevice(vkDevice), |
| m_compositorVkQueue(compositorVkQueue), |
| m_compositorVkQueueLock(compositorVkQueueLock), |
| m_swapChainVkQueue(swapChainVkqueue), |
| m_swapChainVkQueueLock(swapChainVkQueueLock), |
| m_vkCommandPool(VK_NULL_HANDLE), |
| m_swapChainStateVk(nullptr), |
| m_compositorVk(nullptr), |
| m_surfaceState(nullptr) { |
| // TODO(kaiyili): validate the capabilites of the passed in Vulkan |
| // components. |
| VkCommandPoolCreateInfo commandPoolCi = { |
| .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, |
| .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, |
| .queueFamilyIndex = m_compositorQueueFamilyIndex, |
| }; |
| VK_CHECK(m_vk.vkCreateCommandPool(m_vkDevice, &commandPoolCi, nullptr, &m_vkCommandPool)); |
| |
| 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_compositionVkSampler)); |
| } |
| |
| DisplayVk::~DisplayVk() { |
| { |
| android::base::AutoLock lock(*m_swapChainVkQueueLock); |
| VK_CHECK(m_vk.vkQueueWaitIdle(m_swapChainVkQueue)); |
| } |
| { |
| android::base::AutoLock lock(*m_compositorVkQueueLock); |
| VK_CHECK(m_vk.vkQueueWaitIdle(m_compositorVkQueue)); |
| } |
| m_postResourceFuture = std::nullopt; |
| m_composeResourceFuture = std::nullopt; |
| m_compositorVkRenderTargets.clear(); |
| m_vk.vkDestroySampler(m_vkDevice, m_compositionVkSampler, nullptr); |
| m_surfaceState.reset(); |
| m_compositorVk.reset(); |
| m_swapChainStateVk.reset(); |
| m_vk.vkDestroyCommandPool(m_vkDevice, m_vkCommandPool, nullptr); |
| } |
| |
| void DisplayVk::bindToSurface(VkSurfaceKHR surface, uint32_t width, uint32_t height) { |
| { |
| android::base::AutoLock lock(*m_compositorVkQueueLock); |
| VK_CHECK(m_vk.vkQueueWaitIdle(m_compositorVkQueue)); |
| } |
| { |
| android::base::AutoLock lock(*m_swapChainVkQueueLock); |
| VK_CHECK(m_vk.vkQueueWaitIdle(m_swapChainVkQueue)); |
| } |
| m_postResourceFuture = std::nullopt; |
| m_composeResourceFuture = std::nullopt; |
| m_compositorVkRenderTargets = std::deque<std::shared_ptr<CompositorVkRenderTarget>>( |
| k_compositorVkRenderTargetCacheSize, nullptr); |
| m_compositorVk.reset(); |
| m_swapChainStateVk.reset(); |
| |
| if (!SwapChainStateVk::validateQueueFamilyProperties(m_vk, m_vkPhysicalDevice, surface, |
| m_swapChainQueueFamilyIndex)) { |
| GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) |
| << "DisplayVk can't create VkSwapchainKHR with given VkDevice and VkSurfaceKHR."; |
| } |
| auto swapChainCi = SwapChainStateVk::createSwapChainCi( |
| m_vk, surface, m_vkPhysicalDevice, width, height, |
| {m_swapChainQueueFamilyIndex, m_compositorQueueFamilyIndex}); |
| VkFormatProperties formatProps; |
| m_vk.vkGetPhysicalDeviceFormatProperties(m_vkPhysicalDevice, swapChainCi->imageFormat, |
| &formatProps); |
| if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { |
| GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) |
| << "DisplayVk: The image format chosen for present VkImage can't be used as the color " |
| "attachment, and therefore can't be used as the render target of CompositorVk."; |
| } |
| m_swapChainStateVk = std::make_unique<SwapChainStateVk>(m_vk, m_vkDevice, *swapChainCi); |
| m_compositorVk = CompositorVk::create( |
| m_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue, m_compositorVkQueueLock, |
| k_compositorVkRenderTargetFormat, VK_IMAGE_LAYOUT_UNDEFINED, |
| VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_swapChainStateVk->getVkImageViews().size(), |
| m_vkCommandPool, m_compositionVkSampler); |
| |
| int numSwapChainImages = m_swapChainStateVk->getVkImages().size(); |
| |
| m_postResourceFuture = std::async(std::launch::deferred, [this] { |
| return PostResource::create(m_vk, m_vkDevice, m_vkCommandPool); |
| }).share(); |
| m_postResourceFuture.value().wait(); |
| |
| m_inFlightFrameIndex = 0; |
| |
| m_composeResourceFuture = std::async(std::launch::deferred, [this] { |
| return ComposeResource::create(m_vk, m_vkDevice, m_vkCommandPool); |
| }); |
| m_composeResourceFuture.value().wait(); |
| auto surfaceState = std::make_unique<SurfaceState>(); |
| surfaceState->m_height = height; |
| surfaceState->m_width = width; |
| m_surfaceState = std::move(surfaceState); |
| } |
| |
| std::shared_ptr<DisplayVk::DisplayBufferInfo> DisplayVk::createDisplayBuffer( |
| VkImage image, const VkImageCreateInfo &vkImageCreateInfo) { |
| return std::shared_ptr<DisplayBufferInfo>( |
| new DisplayBufferInfo(m_vk, m_vkDevice, vkImageCreateInfo, image)); |
| } |
| |
| std::tuple<bool, std::shared_future<void>> DisplayVk::post( |
| std::shared_ptr<DisplayBufferInfo> displayBufferPtr) { |
| auto completedFuture = std::async(std::launch::deferred, [] {}).share(); |
| completedFuture.wait(); |
| if (!displayBufferPtr) { |
| fprintf(stderr, "%s: warning: null ptr passed to post buffer\n", __func__); |
| return std::make_tuple(true, std::move(completedFuture)); |
| } |
| if (!m_swapChainStateVk || !m_surfaceState) { |
| DISPLAY_VK_ERROR("Haven't bound to a surface, can't post ColorBuffer."); |
| return std::make_tuple(true, std::move(completedFuture)); |
| } |
| if (!canPost(displayBufferPtr->m_vkImageCreateInfo)) { |
| DISPLAY_VK_ERROR("Can't post ColorBuffer."); |
| return std::make_tuple(true, std::move(completedFuture)); |
| } |
| |
| std::shared_ptr<PostResource> postResource = m_postResourceFuture.value().get(); |
| VkSemaphore imageReadySem = postResource->m_swapchainImageAcquireSemaphore; |
| |
| uint32_t imageIndex; |
| VkResult acquireRes = |
| m_vk.vkAcquireNextImageKHR(m_vkDevice, m_swapChainStateVk->getSwapChain(), UINT64_MAX, |
| imageReadySem, VK_NULL_HANDLE, &imageIndex); |
| if (shouldRecreateSwapchain(acquireRes)) { |
| return std::make_tuple(false, std::shared_future<void>()); |
| } |
| VK_CHECK(acquireRes); |
| |
| VkCommandBuffer cmdBuff = postResource->m_vkCommandBuffer; |
| VK_CHECK(m_vk.vkResetCommandBuffer(cmdBuff, 0)); |
| VkCommandBufferBeginInfo beginInfo = { |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, |
| .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, |
| }; |
| VK_CHECK(m_vk.vkBeginCommandBuffer(cmdBuff, &beginInfo)); |
| VkImageMemoryBarrier presentToXferDstBarrier = { |
| .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, |
| .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, |
| .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, |
| .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, |
| .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, |
| .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |
| .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |
| .image = m_swapChainStateVk->getVkImages()[imageIndex], |
| .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .baseMipLevel = 0, |
| .levelCount = 1, |
| .baseArrayLayer = 0, |
| .layerCount = 1}}; |
| m_vk.vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT, |
| VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, |
| &presentToXferDstBarrier); |
| VkImageBlit region = { |
| .srcSubresource = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .mipLevel = 0, |
| .baseArrayLayer = 0, |
| .layerCount = 1}, |
| .srcOffsets = {{0, 0, 0}, |
| {static_cast<int32_t>(displayBufferPtr->m_vkImageCreateInfo.extent.width), |
| static_cast<int32_t>(displayBufferPtr->m_vkImageCreateInfo.extent.height), |
| 1}}, |
| .dstSubresource = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .mipLevel = 0, |
| .baseArrayLayer = 0, |
| .layerCount = 1}, |
| .dstOffsets = {{0, 0, 0}, |
| {static_cast<int32_t>(m_surfaceState->m_width), |
| static_cast<int32_t>(m_surfaceState->m_height), 1}}, |
| }; |
| VkFormat displayBufferFormat = displayBufferPtr->m_vkImageCreateInfo.format; |
| VkImageTiling displayBufferTiling = displayBufferPtr->m_vkImageCreateInfo.tiling; |
| VkFilter filter = VK_FILTER_NEAREST; |
| VkFormatFeatureFlags displayBufferFormatFeatures = |
| getFormatFeatures(displayBufferFormat, displayBufferTiling); |
| if (formatIsDepthOrStencil(displayBufferFormat)) { |
| DISPLAY_VK_ERROR_ONCE( |
| "The format of the display buffer, %s, is a depth/stencil format, we can only use the " |
| "VK_FILTER_NEAREST filter according to VUID-vkCmdBlitImage-srcImage-00232.", |
| string_VkFormat(displayBufferFormat)); |
| filter = VK_FILTER_NEAREST; |
| } else if (!(displayBufferFormatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { |
| DISPLAY_VK_ERROR_ONCE( |
| "The format of the display buffer, %s, with the tiling, %s, doesn't support " |
| "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, so we can only use the " |
| "VK_FILTER_NEAREST filter according VUID-vkCmdBlitImage-filter-02001. The supported " |
| "features are %s.", |
| string_VkFormat(displayBufferFormat), string_VkImageTiling(displayBufferTiling), |
| string_VkFormatFeatureFlags(displayBufferFormatFeatures).c_str()); |
| filter = VK_FILTER_NEAREST; |
| } else { |
| filter = VK_FILTER_LINEAR; |
| } |
| m_vk.vkCmdBlitImage(cmdBuff, displayBufferPtr->m_vkImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |
| m_swapChainStateVk->getVkImages()[imageIndex], |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion, filter); |
| VkImageMemoryBarrier xferDstToPresentBarrier = { |
| .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, |
| .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, |
| .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, |
| .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, |
| .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, |
| .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |
| .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |
| .image = m_swapChainStateVk->getVkImages()[imageIndex], |
| .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .baseMipLevel = 0, |
| .levelCount = 1, |
| .baseArrayLayer = 0, |
| .layerCount = 1}}; |
| m_vk.vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT, |
| VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, |
| &xferDstToPresentBarrier); |
| VK_CHECK(m_vk.vkEndCommandBuffer(cmdBuff)); |
| |
| VkFence postCompleteFence = postResource->m_swapchainImageReleaseFence; |
| VK_CHECK(m_vk.vkResetFences(m_vkDevice, 1, &postCompleteFence)); |
| VkSemaphore postCompleteSemaphore = postResource->m_swapchainImageReleaseSemaphore; |
| VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_TRANSFER_BIT}; |
| VkSubmitInfo submitInfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, |
| .waitSemaphoreCount = 1, |
| .pWaitSemaphores = &imageReadySem, |
| .pWaitDstStageMask = waitStages, |
| .commandBufferCount = 1, |
| .pCommandBuffers = &cmdBuff, |
| .signalSemaphoreCount = 1, |
| .pSignalSemaphores = &postCompleteSemaphore}; |
| { |
| android::base::AutoLock lock(*m_compositorVkQueueLock); |
| VK_CHECK(m_vk.vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo, postCompleteFence)); |
| } |
| std::shared_future<std::shared_ptr<PostResource>> postResourceFuture = |
| std::async(std::launch::deferred, [postCompleteFence, postResource, displayBufferPtr, |
| this]() mutable { |
| VK_CHECK(m_vk.vkWaitForFences(m_vkDevice, 1, &postCompleteFence, VK_TRUE, UINT64_MAX)); |
| // Explicitly reset displayBufferPtr here to make sure the lambda actually capture |
| // displayBufferPtr to correctly extend the lifetime of displayBufferPtr until the |
| // rendering completes. |
| displayBufferPtr.reset(); |
| return postResource; |
| }).share(); |
| m_postResourceFuture = postResourceFuture; |
| |
| auto swapChain = m_swapChainStateVk->getSwapChain(); |
| VkPresentInfoKHR presentInfo = {.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, |
| .waitSemaphoreCount = 1, |
| .pWaitSemaphores = &postCompleteSemaphore, |
| .swapchainCount = 1, |
| .pSwapchains = &swapChain, |
| .pImageIndices = &imageIndex}; |
| VkResult presentRes; |
| { |
| android::base::AutoLock lock(*m_swapChainVkQueueLock); |
| presentRes = m_vk.vkQueuePresentKHR(m_swapChainVkQueue, &presentInfo); |
| } |
| if (shouldRecreateSwapchain(presentRes)) { |
| postResourceFuture.wait(); |
| return std::make_tuple(false, std::shared_future<void>()); |
| } |
| VK_CHECK(presentRes); |
| return std::make_tuple(true, std::async(std::launch::deferred, [postResourceFuture] { |
| // We can't directly wait for the VkFence here, because we |
| // share the VkFences on different frames, but we don't share |
| // the future on different frames. If we directly wait for the |
| // VkFence here, we may wait for a different frame if a new |
| // frame starts to be drawn before this future is waited. |
| postResourceFuture.wait(); |
| }).share()); |
| } |
| |
| std::tuple<bool, std::shared_future<void>> DisplayVk::compose( |
| uint32_t numLayers, const ComposeLayer layers[], |
| std::vector<std::shared_ptr<DisplayBufferInfo>> composeBuffers, |
| std::shared_ptr<DisplayBufferInfo> targetBuffer) { |
| std::shared_future<void> completedFuture = std::async(std::launch::deferred, [] {}).share(); |
| completedFuture.wait(); |
| |
| if (!m_swapChainStateVk || !m_compositorVk) { |
| DISPLAY_VK_ERROR("Haven't bound to a surface, can't compose color buffer."); |
| // The surface hasn't been created yet, hence we don't return |
| // std::nullopt to request rebinding. |
| return std::make_tuple(true, std::move(completedFuture)); |
| } |
| |
| std::vector<std::unique_ptr<ComposeLayerVk>> composeLayers; |
| for (int i = 0; i < numLayers; ++i) { |
| if (layers[i].cbHandle == 0) { |
| // When ColorBuffer handle is 0, it's expected that no ColorBuffer |
| // is not found. |
| continue; |
| } |
| if (!composeBuffers[i]) { |
| DISPLAY_VK_ERROR("warning: null ptr passed to compose buffer for layer %d.", i); |
| continue; |
| } |
| const auto &db = *composeBuffers[i]; |
| if (!canCompositeFrom(db.m_vkImageCreateInfo)) { |
| DISPLAY_VK_ERROR("Can't composite from a display buffer. Skip the layer."); |
| continue; |
| } |
| auto layer = ComposeLayerVk::createFromHwc2ComposeLayer( |
| m_compositionVkSampler, composeBuffers[i]->m_vkImageView, layers[i], |
| composeBuffers[i]->m_vkImageCreateInfo.extent.width, |
| composeBuffers[i]->m_vkImageCreateInfo.extent.height, |
| targetBuffer->m_vkImageCreateInfo.extent.width, |
| targetBuffer->m_vkImageCreateInfo.extent.height); |
| composeLayers.emplace_back(std::move(layer)); |
| } |
| |
| if (composeLayers.empty()) { |
| return std::make_tuple(true, std::move(completedFuture)); |
| } |
| |
| if (!targetBuffer) { |
| DISPLAY_VK_ERROR("warning null ptr passed to compose target."); |
| return std::make_tuple(true, std::move(completedFuture)); |
| } |
| if (!canCompositeTo(targetBuffer->m_vkImageCreateInfo)) { |
| DISPLAY_VK_ERROR("Can't write the result of the composition to the display buffer."); |
| return std::make_tuple(true, std::move(completedFuture)); |
| } |
| |
| std::shared_ptr<CompositorVkRenderTarget> compositorVkRenderTarget = |
| targetBuffer->m_compositorVkRenderTarget.lock(); |
| if (!compositorVkRenderTarget) { |
| compositorVkRenderTarget = m_compositorVk->createRenderTarget( |
| targetBuffer->m_vkImageView, targetBuffer->m_vkImageCreateInfo.extent.width, |
| targetBuffer->m_vkImageCreateInfo.extent.height); |
| if (!compositorVkRenderTarget) { |
| GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) |
| << "Failed to create CompositorVkRenderTarget for the target display buffer."; |
| } |
| m_compositorVkRenderTargets.pop_back(); |
| m_compositorVkRenderTargets.push_front(compositorVkRenderTarget); |
| targetBuffer->m_compositorVkRenderTarget = compositorVkRenderTarget; |
| } |
| |
| std::future<std::unique_ptr<ComposeResource>> composeResourceFuture = |
| std::move(m_composeResourceFuture.value()); |
| if (!composeResourceFuture.valid()) { |
| GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) |
| << "Invalid composeResourceFuture in m_postResourceFutures."; |
| } |
| std::unique_ptr<ComposeResource> composeResource = composeResourceFuture.get(); |
| |
| if (compareAndSaveComposition(m_inFlightFrameIndex, numLayers, layers, composeBuffers)) { |
| auto composition = std::make_unique<Composition>(std::move(composeLayers)); |
| m_compositorVk->setComposition(m_inFlightFrameIndex, std::move(composition)); |
| } |
| |
| VkCommandBuffer cmdBuff = composeResource->m_vkCommandBuffer; |
| VK_CHECK(m_vk.vkResetCommandBuffer(cmdBuff, 0)); |
| |
| VkCommandBufferBeginInfo beginInfo = { |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, |
| .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, |
| }; |
| VK_CHECK(m_vk.vkBeginCommandBuffer(cmdBuff, &beginInfo)); |
| m_compositorVk->recordCommandBuffers(m_inFlightFrameIndex, cmdBuff, *compositorVkRenderTarget); |
| // Insert a VkImageMemoryBarrier so that the vkCmdBlitImage in post will wait for the rendering |
| // to the render target to complete. |
| 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 = targetBuffer->m_vkImage, |
| .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .baseMipLevel = 0, |
| .levelCount = 1, |
| .baseArrayLayer = 0, |
| .layerCount = 1}}; |
| m_vk.vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, |
| VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, |
| &renderTargetBarrier); |
| VK_CHECK(m_vk.vkEndCommandBuffer(cmdBuff)); |
| |
| VkFence composeCompleteFence = composeResource->m_composeCompleteFence; |
| VK_CHECK(m_vk.vkResetFences(m_vkDevice, 1, &composeCompleteFence)); |
| VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_TRANSFER_BIT}; |
| VkSubmitInfo submitInfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, |
| .waitSemaphoreCount = 0, |
| .pWaitSemaphores = nullptr, |
| .pWaitDstStageMask = waitStages, |
| .commandBufferCount = 1, |
| .pCommandBuffers = &cmdBuff, |
| .signalSemaphoreCount = 0, |
| .pSignalSemaphores = nullptr}; |
| { |
| android::base::AutoLock lock(*m_compositorVkQueueLock); |
| VK_CHECK(m_vk.vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo, composeCompleteFence)); |
| } |
| |
| m_composeResourceFuture = |
| std::async(std::launch::deferred, |
| [composeCompleteFence, composeResource = std::move(composeResource), |
| composeBuffers = std::move(composeBuffers), targetBuffer, this]() mutable { |
| VK_CHECK(m_vk.vkWaitForFences(m_vkDevice, 1, &composeCompleteFence, VK_TRUE, |
| UINT64_MAX)); |
| // Explicitly clear the composeBuffers here to ensure the lambda does |
| // caputure composeBuffers and correctly extend the lifetime of related |
| // DisplayBufferInfo until the render completes. |
| composeBuffers.clear(); |
| // Explicitly reset the targetBuffer here to ensure the lambda does caputure |
| // targetBuffer and correctly extend the lifetime of the DisplayBufferInfo |
| // until the render completes. |
| targetBuffer.reset(); |
| return std::move(composeResource); |
| }); |
| m_inFlightFrameIndex = (m_inFlightFrameIndex + 1) % m_swapChainStateVk->getVkImages().size(); |
| return post(targetBuffer); |
| } |
| |
| VkFormatFeatureFlags DisplayVk::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 { |
| DISPLAY_VK_ERROR("Unknown tiling %#" PRIx64 ".", static_cast<uint64_t>(tiling)); |
| } |
| return formatFeatures; |
| } |
| |
| bool DisplayVk::canPost(const VkImageCreateInfo &postImageCi) { |
| // According to VUID-vkCmdBlitImage-srcImage-01999, the format features of srcImage must contain |
| // VK_FORMAT_FEATURE_BLIT_SRC_BIT. |
| VkFormatFeatureFlags formatFeatures = getFormatFeatures(postImageCi.format, postImageCi.tiling); |
| if (!(formatFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) { |
| DISPLAY_VK_ERROR( |
| "VK_FORMAT_FEATURE_BLIT_SRC_BLIT is not supported for VkImage with format %s, tilling " |
| "%s. Supported features are %s.", |
| string_VkFormat(postImageCi.format), string_VkImageTiling(postImageCi.tiling), |
| string_VkFormatFeatureFlags(formatFeatures).c_str()); |
| return false; |
| } |
| |
| // According to VUID-vkCmdBlitImage-srcImage-06421, srcImage must not use a format that requires |
| // a sampler Y’CBCR conversion. |
| if (formatRequiresSamplerYcbcrConversion(postImageCi.format)) { |
| DISPLAY_VK_ERROR("Format %s requires a sampler Y'CbCr conversion. Can't be used to post.", |
| string_VkFormat(postImageCi.format)); |
| return false; |
| } |
| |
| if (!(postImageCi.usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT)) { |
| // According to VUID-vkCmdBlitImage-srcImage-00219, srcImage must have been created with |
| // VK_IMAGE_USAGE_TRANSFER_SRC_BIT usage flag. |
| DISPLAY_VK_ERROR( |
| "The VkImage is not created with the VK_IMAGE_USAGE_TRANSFER_SRC_BIT usage flag. The " |
| "usage flags are %s.", |
| string_VkImageUsageFlags(postImageCi.usage).c_str()); |
| return false; |
| } |
| |
| VkFormat swapChainFormat = m_swapChainStateVk->getFormat(); |
| if (formatIsSInt(postImageCi.format) || formatIsSInt(swapChainFormat)) { |
| // According to VUID-vkCmdBlitImage-srcImage-00229, if either of srcImage or dstImage was |
| // created with a signed integer VkFormat, the other must also have been created with a |
| // signed integer VkFormat. |
| if (!(formatIsSInt(postImageCi.format) && formatIsSInt(m_swapChainStateVk->getFormat()))) { |
| DISPLAY_VK_ERROR( |
| "The format(%s) doesn't match with the format of the presentable image(%s): either " |
| "of the formats is a signed integer VkFormat, but the other is not.", |
| string_VkFormat(postImageCi.format), string_VkFormat(swapChainFormat)); |
| return false; |
| } |
| } |
| |
| if (formatIsUInt(postImageCi.format) || formatIsUInt(swapChainFormat)) { |
| // According to VUID-vkCmdBlitImage-srcImage-00230, if either of srcImage or dstImage was |
| // created with an unsigned integer VkFormat, the other must also have been created with an |
| // unsigned integer VkFormat. |
| if (!(formatIsUInt(postImageCi.format) && formatIsUInt(swapChainFormat))) { |
| DISPLAY_VK_ERROR( |
| "The format(%s) doesn't match with the format of the presentable image(%s): either " |
| "of the formats is an unsigned integer VkFormat, but the other is not.", |
| string_VkFormat(postImageCi.format), string_VkFormat(swapChainFormat)); |
| return false; |
| } |
| } |
| |
| if (formatIsDepthOrStencil(postImageCi.format) || formatIsDepthOrStencil(swapChainFormat)) { |
| // According to VUID-vkCmdBlitImage-srcImage-00231, if either of srcImage or dstImage was |
| // created with a depth/stencil format, the other must have exactly the same format. |
| if (postImageCi.format != swapChainFormat) { |
| DISPLAY_VK_ERROR( |
| "The format(%s) doesn't match with the format of the presentable image(%s): either " |
| "of the formats is a depth/stencil VkFormat, but the other is not the same format.", |
| string_VkFormat(postImageCi.format), string_VkFormat(swapChainFormat)); |
| return false; |
| } |
| } |
| |
| if (postImageCi.samples != VK_SAMPLE_COUNT_1_BIT) { |
| // According to VUID-vkCmdBlitImage-srcImage-00233, srcImage must have been created with a |
| // samples value of VK_SAMPLE_COUNT_1_BIT. |
| DISPLAY_VK_ERROR( |
| "The VkImage is not created with the VK_SAMPLE_COUNT_1_BIT samples value. The samples " |
| "value is %s.", |
| string_VkSampleCountFlagBits(postImageCi.samples)); |
| return false; |
| } |
| if (postImageCi.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) { |
| // According to VUID-vkCmdBlitImage-dstImage-02545, dstImage and srcImage must not have been |
| // created with flags containing VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT. |
| DISPLAY_VK_ERROR( |
| "The VkImage can't be created with flags containing " |
| "VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT. The flags are %s.", |
| string_VkImageCreateFlags(postImageCi.flags).c_str()); |
| return false; |
| } |
| return true; |
| } |
| |
| bool DisplayVk::canCompositeFrom(const VkImageCreateInfo &imageCi) { |
| VkFormatFeatureFlags formatFeatures = getFormatFeatures(imageCi.format, imageCi.tiling); |
| if (!(formatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { |
| DISPLAY_VK_ERROR( |
| "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 DisplayVk::canCompositeTo(const VkImageCreateInfo &imageCi) { |
| VkFormatFeatureFlags formatFeatures = getFormatFeatures(imageCi.format, imageCi.tiling); |
| if (!(formatFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { |
| DISPLAY_VK_ERROR( |
| "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)) { |
| DISPLAY_VK_ERROR( |
| "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 (imageCi.format != k_compositorVkRenderTargetFormat) { |
| DISPLAY_VK_ERROR( |
| "The format of the image, %s, is not supported by the CompositorVk as the render " |
| "target.", |
| string_VkFormat(imageCi.format)); |
| return false; |
| } |
| return true; |
| } |
| |
| bool DisplayVk::compareAndSaveComposition( |
| uint32_t renderTargetIndex, uint32_t numLayers, const ComposeLayer layers[], |
| const std::vector<std::shared_ptr<DisplayBufferInfo>> &composeBuffers) { |
| if (!m_surfaceState) { |
| GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) |
| << "Haven't bound to a surface, can't compare and save composition."; |
| } |
| auto [iPrevComposition, compositionNotFound] = |
| m_surfaceState->m_prevCompositions.emplace(renderTargetIndex, 0); |
| auto &prevComposition = iPrevComposition->second; |
| bool compositionChanged = false; |
| if (numLayers == prevComposition.size()) { |
| for (int i = 0; i < numLayers; i++) { |
| if (composeBuffers[i] == nullptr) { |
| // If the display buffer of the current layer doesn't exist, we |
| // check if the layer at the same index in the previous |
| // composition doesn't exist either. |
| if (prevComposition[i] == nullptr) { |
| continue; |
| } else { |
| compositionChanged = true; |
| break; |
| } |
| } |
| if (prevComposition[i] == nullptr) { |
| // If the display buffer of the current layer exists but the |
| // layer at the same index in the previous composition doesn't |
| // exist, the composition is changed. |
| compositionChanged = true; |
| break; |
| } |
| const auto &prevLayer = *prevComposition[i]; |
| const auto prevDisplayBufferPtr = prevLayer.m_displayBuffer.lock(); |
| // prevLayer.m_displayBuffer is a weak pointer, so if |
| // prevDisplayBufferPtr is null, the color buffer |
| // prevDisplayBufferPtr pointed to should have been released or |
| // re-allocated, and we should consider the composition is changed. |
| // If prevDisplayBufferPtr exists and it points to the same display |
| // buffer as the input composeBuffers[i] we consider the composition |
| // not changed. |
| if (!prevDisplayBufferPtr || prevDisplayBufferPtr != composeBuffers[i]) { |
| compositionChanged = true; |
| break; |
| } |
| const auto &prevHwc2Layer = prevLayer.m_hwc2Layer; |
| const auto hwc2Layer = layers[i]; |
| compositionChanged = |
| (prevHwc2Layer.cbHandle != hwc2Layer.cbHandle) || |
| (prevHwc2Layer.composeMode != hwc2Layer.composeMode) || |
| (prevHwc2Layer.displayFrame.left != hwc2Layer.displayFrame.left) || |
| (prevHwc2Layer.displayFrame.top != hwc2Layer.displayFrame.top) || |
| (prevHwc2Layer.displayFrame.right != hwc2Layer.displayFrame.right) || |
| (prevHwc2Layer.displayFrame.bottom != hwc2Layer.displayFrame.bottom) || |
| (prevHwc2Layer.crop.left != hwc2Layer.crop.left) || |
| (prevHwc2Layer.crop.top != hwc2Layer.crop.top) || |
| (prevHwc2Layer.crop.right != hwc2Layer.crop.right) || |
| (prevHwc2Layer.crop.bottom != hwc2Layer.crop.bottom) || |
| (prevHwc2Layer.blendMode != hwc2Layer.blendMode) || |
| (prevHwc2Layer.alpha != hwc2Layer.alpha) || |
| (prevHwc2Layer.color.r != hwc2Layer.color.r) || |
| (prevHwc2Layer.color.g != hwc2Layer.color.g) || |
| (prevHwc2Layer.color.b != hwc2Layer.color.b) || |
| (prevHwc2Layer.color.a != hwc2Layer.color.a) || |
| (prevHwc2Layer.transform != hwc2Layer.transform); |
| if (compositionChanged) { |
| break; |
| } |
| } |
| } else { |
| compositionChanged = true; |
| } |
| bool needsSave = compositionNotFound || compositionChanged; |
| if (needsSave) { |
| prevComposition.clear(); |
| for (int i = 0; i < numLayers; i++) { |
| if (composeBuffers[i] == nullptr) { |
| prevComposition.emplace_back(nullptr); |
| continue; |
| } |
| auto layer = std::make_unique<SurfaceState::Layer>(); |
| layer->m_hwc2Layer = layers[i]; |
| layer->m_displayBuffer = composeBuffers[i]; |
| prevComposition.emplace_back(std::move(layer)); |
| } |
| } |
| return needsSave; |
| } |
| |
| DisplayVk::DisplayBufferInfo::DisplayBufferInfo(const goldfish_vk::VulkanDispatch &vk, |
| VkDevice vkDevice, |
| const VkImageCreateInfo &vkImageCreateInfo, |
| VkImage image) |
| : m_vk(vk), |
| m_vkDevice(vkDevice), |
| m_vkImageCreateInfo(vk_make_orphan_copy(vkImageCreateInfo)), |
| m_vkImage(image), |
| m_vkImageView(VK_NULL_HANDLE), |
| m_compositorVkRenderTarget() { |
| VkImageViewCreateInfo imageViewCi = { |
| .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
| .image = image, |
| .viewType = VK_IMAGE_VIEW_TYPE_2D, |
| .format = m_vkImageCreateInfo.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_vkImageView)); |
| } |
| |
| DisplayVk::DisplayBufferInfo::~DisplayBufferInfo() { |
| m_vk.vkDestroyImageView(m_vkDevice, m_vkImageView, nullptr); |
| } |
| |
| std::shared_ptr<DisplayVk::PostResource> DisplayVk::PostResource::create( |
| const goldfish_vk::VulkanDispatch &vk, VkDevice vkDevice, VkCommandPool vkCommandPool) { |
| VkFenceCreateInfo fenceCi = { |
| .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, |
| }; |
| VkFence fence; |
| VK_CHECK(vk.vkCreateFence(vkDevice, &fenceCi, nullptr, &fence)); |
| VkSemaphore semaphores[2]; |
| for (uint32_t i = 0; i < std::size(semaphores); i++) { |
| VkSemaphoreCreateInfo semaphoreCi = { |
| .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, |
| }; |
| VK_CHECK(vk.vkCreateSemaphore(vkDevice, &semaphoreCi, nullptr, &semaphores[i])); |
| } |
| VkCommandBuffer commandBuffer; |
| VkCommandBufferAllocateInfo commandBufferAllocInfo = { |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, |
| .commandPool = vkCommandPool, |
| .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, |
| .commandBufferCount = 1, |
| }; |
| VK_CHECK(vk.vkAllocateCommandBuffers(vkDevice, &commandBufferAllocInfo, &commandBuffer)); |
| return std::shared_ptr<PostResource>(new PostResource( |
| vk, vkDevice, vkCommandPool, fence, semaphores[0], semaphores[1], commandBuffer)); |
| } |
| |
| DisplayVk::PostResource::~PostResource() { |
| m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool, 1, &m_vkCommandBuffer); |
| m_vk.vkDestroyFence(m_vkDevice, m_swapchainImageReleaseFence, nullptr); |
| m_vk.vkDestroySemaphore(m_vkDevice, m_swapchainImageAcquireSemaphore, nullptr); |
| m_vk.vkDestroySemaphore(m_vkDevice, m_swapchainImageReleaseSemaphore, nullptr); |
| } |
| |
| DisplayVk::PostResource::PostResource(const goldfish_vk::VulkanDispatch &vk, VkDevice vkDevice, |
| VkCommandPool vkCommandPool, |
| VkFence swapchainImageReleaseFence, |
| VkSemaphore swapchainImageAcquireSemaphore, |
| VkSemaphore swapchainImageReleaseSemaphore, |
| VkCommandBuffer vkCommandBuffer) |
| : m_swapchainImageReleaseFence(swapchainImageReleaseFence), |
| m_swapchainImageAcquireSemaphore(swapchainImageAcquireSemaphore), |
| m_swapchainImageReleaseSemaphore(swapchainImageReleaseSemaphore), |
| m_vkCommandBuffer(vkCommandBuffer), |
| m_vk(vk), |
| m_vkDevice(vkDevice), |
| m_vkCommandPool(vkCommandPool) {} |
| |
| std::unique_ptr<DisplayVk::ComposeResource> DisplayVk::ComposeResource::create( |
| const goldfish_vk::VulkanDispatch &vk, VkDevice vkDevice, VkCommandPool vkCommandPool) { |
| VkFenceCreateInfo fenceCi = { |
| .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, |
| }; |
| VkFence fence; |
| VK_CHECK(vk.vkCreateFence(vkDevice, &fenceCi, nullptr, &fence)); |
| |
| VkCommandBuffer commandBuffer; |
| VkCommandBufferAllocateInfo commandBufferAllocInfo = { |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, |
| .commandPool = vkCommandPool, |
| .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, |
| .commandBufferCount = 1, |
| }; |
| VK_CHECK(vk.vkAllocateCommandBuffers(vkDevice, &commandBufferAllocInfo, &commandBuffer)); |
| |
| return std::unique_ptr<ComposeResource>( |
| new ComposeResource(vk, vkDevice, vkCommandPool, fence, commandBuffer)); |
| } |
| |
| DisplayVk::ComposeResource::~ComposeResource() { |
| m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool, 1, &m_vkCommandBuffer); |
| m_vk.vkDestroyFence(m_vkDevice, m_composeCompleteFence, nullptr); |
| } |
| |
| DisplayVk::ComposeResource::ComposeResource(const goldfish_vk::VulkanDispatch &vk, |
| VkDevice vkDevice, VkCommandPool vkCommandPool, |
| VkFence composeCompleteFence, |
| VkCommandBuffer vkCommandBuffer) |
| : m_composeCompleteFence(composeCompleteFence), |
| m_vkCommandBuffer(vkCommandBuffer), |
| m_vk(vk), |
| m_vkDevice(vkDevice), |
| m_vkCommandPool(vkCommandPool) {} |