| #include "DisplayVk.h" |
| |
| #include <algorithm> |
| #include <glm/glm.hpp> |
| #include <glm/gtx/matrix_transform_2d.hpp> |
| |
| #include "host-common/GfxstreamFatalError.h" |
| #include "host-common/logging.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) { |
| switch (result) { |
| case VK_SUBOPTIMAL_KHR: |
| case VK_ERROR_OUT_OF_DATE_KHR: |
| // b/217229121: drivers may return VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT in |
| // vkQueuePresentKHR even if VK_EXT_full_screen_exclusive is not enabled. |
| case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| } // 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_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)); |
| } |
| |
| DisplayVk::~DisplayVk() { |
| { |
| android::base::AutoLock lock(*m_swapChainVkQueueLock); |
| VK_CHECK(vk_util::waitForVkQueueIdleWithRetry(m_vk, m_swapChainVkQueue)); |
| } |
| { |
| android::base::AutoLock lock(*m_compositorVkQueueLock); |
| VK_CHECK(vk_util::waitForVkQueueIdleWithRetry(m_vk, m_compositorVkQueue)); |
| } |
| m_postResourceFuture = std::nullopt; |
| m_surfaceState.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(vk_util::waitForVkQueueIdleWithRetry(m_vk, m_compositorVkQueue)); |
| } |
| { |
| android::base::AutoLock lock(*m_swapChainVkQueueLock); |
| VK_CHECK(vk_util::waitForVkQueueIdleWithRetry(m_vk, m_swapChainVkQueue)); |
| } |
| m_postResourceFuture = std::nullopt; |
| 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}); |
| if (!swapChainCi) { |
| GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) |
| << "Failed to create VkSwapchainCreateInfoKHR."; |
| } |
| VkFormatProperties formatProps; |
| m_vk.vkGetPhysicalDeviceFormatProperties(m_vkPhysicalDevice, |
| swapChainCi->mCreateInfo.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->mCreateInfo); |
| |
| 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; |
| |
| auto surfaceState = std::make_unique<SurfaceState>(); |
| surfaceState->m_height = height; |
| surfaceState->m_width = width; |
| m_surfaceState = std::move(surfaceState); |
| } |
| |
| std::tuple<bool, std::shared_future<void>> DisplayVk::post( |
| const BorrowedImageInfo* sourceImageInfo) { |
| auto completedFuture = std::async(std::launch::deferred, [] {}).share(); |
| completedFuture.wait(); |
| |
| 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)); |
| } |
| |
| const auto* sourceImageInfoVk = static_cast<const BorrowedImageInfoVk*>(sourceImageInfo); |
| if (!canPost(sourceImageInfoVk->imageCreateInfo)) { |
| 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)); |
| |
| 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(cmdBuff, &beginInfo)); |
| |
| std::vector<VkImageMemoryBarrier> preBlitQueueTransferBarriers; |
| std::vector<VkImageMemoryBarrier> preBlitLayoutTransitionBarriers; |
| std::vector<VkImageMemoryBarrier> postBlitLayoutTransitionBarriers; |
| std::vector<VkImageMemoryBarrier> postBlitQueueTransferBarriers; |
| addNeededBarriersToUseBorrowedImage( |
| *sourceImageInfoVk, m_compositorQueueFamilyIndex, |
| /*usedInitialImageLayout=*/VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |
| /*usedFinalImageLayout=*/VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |
| &preBlitQueueTransferBarriers, &preBlitLayoutTransitionBarriers, |
| &postBlitLayoutTransitionBarriers, &postBlitQueueTransferBarriers); |
| preBlitLayoutTransitionBarriers.push_back( |
| VkImageMemoryBarrier{.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, |
| .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, |
| .dstAccessMask = VK_ACCESS_MEMORY_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 = m_swapChainStateVk->getVkImages()[imageIndex], |
| .subresourceRange = { |
| .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .baseMipLevel = 0, |
| .levelCount = 1, |
| .baseArrayLayer = 0, |
| .layerCount = 1, |
| }}); |
| postBlitLayoutTransitionBarriers.push_back(VkImageMemoryBarrier{ |
| .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, |
| }, |
| }); |
| |
| if (!preBlitQueueTransferBarriers.empty()) { |
| m_vk.vkCmdPipelineBarrier( |
| cmdBuff, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, |
| VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, |
| static_cast<uint32_t>(preBlitQueueTransferBarriers.size()), |
| preBlitQueueTransferBarriers.data()); |
| } |
| if (!preBlitLayoutTransitionBarriers.empty()) { |
| m_vk.vkCmdPipelineBarrier( |
| cmdBuff, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, |
| VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, |
| static_cast<uint32_t>(preBlitLayoutTransitionBarriers.size()), |
| preBlitLayoutTransitionBarriers.data()); |
| } |
| |
| const VkImageBlit region = { |
| .srcSubresource = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .mipLevel = 0, |
| .baseArrayLayer = 0, |
| .layerCount = 1}, |
| .srcOffsets = {{0, 0, 0}, |
| {static_cast<int32_t>(sourceImageInfoVk->imageCreateInfo.extent.width), |
| static_cast<int32_t>(sourceImageInfoVk->imageCreateInfo.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 = sourceImageInfoVk->imageCreateInfo.format; |
| VkImageTiling displayBufferTiling = sourceImageInfoVk->imageCreateInfo.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, sourceImageInfoVk->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |
| m_swapChainStateVk->getVkImages()[imageIndex], |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion, filter); |
| |
| if (!postBlitLayoutTransitionBarriers.empty()) { |
| m_vk.vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT, |
| VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, |
| static_cast<uint32_t>(postBlitLayoutTransitionBarriers.size()), |
| postBlitLayoutTransitionBarriers.data()); |
| } |
| if (!postBlitQueueTransferBarriers.empty()) { |
| m_vk.vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT, |
| VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, |
| static_cast<uint32_t>(postBlitQueueTransferBarriers.size()), |
| postBlitQueueTransferBarriers.data()); |
| } |
| |
| 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, this]() mutable { |
| VK_CHECK(m_vk.vkWaitForFences(m_vkDevice, 1, &postCompleteFence, VK_TRUE, UINT64_MAX)); |
| 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()); |
| } |
| |
| 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; |
| } |
| |
| 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) {} |