#include "SwapChainStateVk.h" #include #include #include "host-common/GfxstreamFatalError.h" #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; #define SWAPCHAINSTATE_VK_ERROR(fmt, ...) \ do { \ fprintf(stderr, "%s(%s:%d): " fmt "\n", __func__, __FILE__, __LINE__, ##__VA_ARGS__); \ fflush(stderr); \ } while (0) namespace { void swap(SwapchainCreateInfoWrapper& a, SwapchainCreateInfoWrapper& b) { std::swap(a.mQueueFamilyIndices, b.mQueueFamilyIndices); std::swap(a.mCreateInfo, b.mCreateInfo); // The C++ spec guarantees that after std::swap is called, all iterators and references of the // container remain valid, and the past-the-end iterator is invalidated. Therefore, no need to // reset the VkSwapchainCreateInfoKHR::pQueueFamilyIndices. } } // namespace SwapchainCreateInfoWrapper::SwapchainCreateInfoWrapper(const VkSwapchainCreateInfoKHR& createInfo) : mCreateInfo(createInfo) { if (createInfo.pNext) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "VkSwapchainCreateInfoKHR with pNext in the chain is not supported."; } if (createInfo.pQueueFamilyIndices && (createInfo.queueFamilyIndexCount > 0)) { setQueueFamilyIndices(std::vector( createInfo.pQueueFamilyIndices, createInfo.pQueueFamilyIndices + createInfo.queueFamilyIndexCount)); } else { setQueueFamilyIndices({}); } } SwapchainCreateInfoWrapper::SwapchainCreateInfoWrapper(const SwapchainCreateInfoWrapper& other) : mCreateInfo(other.mCreateInfo) { if (other.mCreateInfo.pNext) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "VkSwapchainCreateInfoKHR with pNext in the chain is not supported."; } setQueueFamilyIndices(other.mQueueFamilyIndices); } SwapchainCreateInfoWrapper& SwapchainCreateInfoWrapper::operator=( const SwapchainCreateInfoWrapper& other) { SwapchainCreateInfoWrapper tmp(other); swap(*this, tmp); return *this; } void SwapchainCreateInfoWrapper::setQueueFamilyIndices( const std::vector& queueFamilyIndices) { mQueueFamilyIndices = queueFamilyIndices; mCreateInfo.queueFamilyIndexCount = static_cast(mQueueFamilyIndices.size()); if (mQueueFamilyIndices.empty()) { mCreateInfo.pQueueFamilyIndices = nullptr; } else { mCreateInfo.pQueueFamilyIndices = queueFamilyIndices.data(); } } std::unique_ptr SwapChainStateVk::createSwapChainVk( const VulkanDispatch& vk, VkDevice vkDevice, const VkSwapchainCreateInfoKHR& swapChainCi) { std::unique_ptr swapChainVk(new SwapChainStateVk(vk, vkDevice)); if (swapChainVk->initSwapChainStateVk(swapChainCi) != VK_SUCCESS) { return nullptr; } return swapChainVk; } SwapChainStateVk::SwapChainStateVk(const VulkanDispatch& vk, VkDevice vkDevice) : m_vk(vk), m_vkDevice(vkDevice), m_vkSwapChain(VK_NULL_HANDLE), m_vkImages(0), m_vkImageViews(0) {} VkResult SwapChainStateVk::initSwapChainStateVk(const VkSwapchainCreateInfoKHR& swapChainCi) { VkResult res = m_vk.vkCreateSwapchainKHR(m_vkDevice, &swapChainCi, nullptr, &m_vkSwapChain); if (res == VK_ERROR_INITIALIZATION_FAILED) return res; VK_CHECK(res); uint32_t imageCount = 0; VK_CHECK(m_vk.vkGetSwapchainImagesKHR(m_vkDevice, m_vkSwapChain, &imageCount, nullptr)); m_vkImageExtent = swapChainCi.imageExtent; m_vkImages.resize(imageCount); VK_CHECK( m_vk.vkGetSwapchainImagesKHR(m_vkDevice, m_vkSwapChain, &imageCount, m_vkImages.data())); for (auto i = 0; i < m_vkImages.size(); i++) { VkImageViewCreateInfo imageViewCi = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = m_vkImages[i], .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = k_vkFormat, .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 vkImageView; VK_CHECK(m_vk.vkCreateImageView(m_vkDevice, &imageViewCi, nullptr, &vkImageView)); m_vkImageViews.push_back(vkImageView); } return VK_SUCCESS; } SwapChainStateVk::~SwapChainStateVk() { for (auto imageView : m_vkImageViews) { m_vk.vkDestroyImageView(m_vkDevice, imageView, nullptr); } if (m_vkSwapChain != VK_NULL_HANDLE) { m_vk.vkDestroySwapchainKHR(m_vkDevice, m_vkSwapChain, nullptr); } } std::vector SwapChainStateVk::getRequiredInstanceExtensions() { return { VK_KHR_SURFACE_EXTENSION_NAME, #ifdef _WIN32 VK_KHR_WIN32_SURFACE_EXTENSION_NAME, #endif #ifdef __APPLE__ VK_EXT_METAL_SURFACE_EXTENSION_NAME, #endif #ifdef VK_USE_PLATFORM_XCB_KHR VK_KHR_XCB_SURFACE_EXTENSION_NAME, #endif }; } std::vector SwapChainStateVk::getRequiredDeviceExtensions() { return { VK_KHR_SWAPCHAIN_EXTENSION_NAME, }; } bool SwapChainStateVk::validateQueueFamilyProperties(const VulkanDispatch& vk, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t queueFamilyIndex) { VkBool32 presentSupport = VK_FALSE; VK_CHECK(vk.vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, &presentSupport)); return presentSupport; } std::optional SwapChainStateVk::createSwapChainCi( const VulkanDispatch& vk, VkSurfaceKHR surface, VkPhysicalDevice physicalDevice, uint32_t width, uint32_t height, const std::unordered_set& queueFamilyIndices) { uint32_t formatCount = 0; VK_CHECK( vk.vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr)); std::vector formats(formatCount); VkResult res = vk.vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, formats.data()); // b/217226027: drivers may return VK_INCOMPLETE with pSurfaceFormatCount returned by // vkGetPhysicalDeviceSurfaceFormatsKHR. Retry here as a work around to the potential driver // bug. if (res == VK_INCOMPLETE) { formatCount = (formatCount + 1) * 2; INFO( "VK_INCOMPLETE returned by vkGetPhysicalDeviceSurfaceFormatsKHR. A possible driver " "bug. Retry with *pSurfaceFormatCount = %" PRIu32 ".", formatCount); formats.resize(formatCount); res = vk.vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, formats.data()); formats.resize(formatCount); } if (res == VK_INCOMPLETE) { INFO( "VK_INCOMPLETE still returned by vkGetPhysicalDeviceSurfaceFormatsKHR with retry. A " "possible driver bug."); } else { VK_CHECK(res); } auto iSurfaceFormat = std::find_if(formats.begin(), formats.end(), [](const VkSurfaceFormatKHR& format) { return format.format == k_vkFormat && format.colorSpace == k_vkColorSpace; }); if (iSurfaceFormat == formats.end()) { SWAPCHAINSTATE_VK_ERROR("Fail to create swapchain: the format(%#" PRIx64 ") with color space(%#" PRIx64 ") not supported.", static_cast(k_vkFormat), static_cast(k_vkColorSpace)); return std::nullopt; } uint32_t presentModeCount = 0; VK_CHECK(vk.vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, nullptr)); std::vector presentModes_(presentModeCount); VK_CHECK(vk.vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes_.data())); std::unordered_set presentModes(presentModes_.begin(), presentModes_.end()); VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; if (!presentModes.count(VK_PRESENT_MODE_FIFO_KHR)) { SWAPCHAINSTATE_VK_ERROR("Fail to create swapchain: FIFO present mode not supported."); return std::nullopt; } VkFormatProperties formatProperties = {}; vk.vkGetPhysicalDeviceFormatProperties(physicalDevice, k_vkFormat, &formatProperties); // According to the spec, a presentable image is equivalent to a non-presentable image created // with the VK_IMAGE_TILING_OPTIMAL tiling parameter. VkFormatFeatureFlags formatFeatures = formatProperties.optimalTilingFeatures; if (!(formatFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) { // According to VUID-vkCmdBlitImage-dstImage-02000, the format features of dstImage must // contain VK_FORMAT_FEATURE_BLIT_DST_BIT. SWAPCHAINSTATE_VK_ERROR( "The format %s with the optimal tiling doesn't support VK_FORMAT_FEATURE_BLIT_DST_BIT. " "The supported features are %s.", string_VkFormat(k_vkFormat), string_VkFormatFeatureFlags(formatFeatures).c_str()); return std::nullopt; } VkSurfaceCapabilitiesKHR surfaceCaps; VK_CHECK(vk.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCaps)); if (!(surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) { SWAPCHAINSTATE_VK_ERROR( "The supported usage flags of the presentable images is %s, and don't contain " "VK_IMAGE_USAGE_TRANSFER_DST_BIT.", string_VkImageUsageFlags(surfaceCaps.supportedUsageFlags).c_str()); return std::nullopt; } std::optional maybeExtent = std::nullopt; if (surfaceCaps.currentExtent.width != UINT32_MAX && surfaceCaps.currentExtent.width == width && surfaceCaps.currentExtent.height == height) { maybeExtent = surfaceCaps.currentExtent; } else if (width >= surfaceCaps.minImageExtent.width && width <= surfaceCaps.maxImageExtent.width && height >= surfaceCaps.minImageExtent.height && height <= surfaceCaps.maxImageExtent.height) { maybeExtent = VkExtent2D({width, height}); } if (!maybeExtent.has_value()) { SWAPCHAINSTATE_VK_ERROR("Fail to create swapchain: extent(%" PRIu64 "x%" PRIu64 ") not supported.", static_cast(width), static_cast(height)); return std::nullopt; } auto extent = maybeExtent.value(); uint32_t imageCount = surfaceCaps.minImageCount + 1; if (surfaceCaps.maxImageCount != 0 && surfaceCaps.maxImageCount < imageCount) { imageCount = surfaceCaps.maxImageCount; } SwapchainCreateInfoWrapper swapChainCi(VkSwapchainCreateInfoKHR{ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .pNext = nullptr, .flags = VkSwapchainCreateFlagsKHR{0}, .surface = surface, .minImageCount = imageCount, .imageFormat = iSurfaceFormat->format, .imageColorSpace = iSurfaceFormat->colorSpace, .imageExtent = extent, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, .imageSharingMode = VkSharingMode{}, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, .preTransform = surfaceCaps.currentTransform, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .presentMode = presentMode, .clipped = VK_TRUE, .oldSwapchain = VK_NULL_HANDLE}); if (queueFamilyIndices.empty()) { SWAPCHAINSTATE_VK_ERROR("Fail to create swapchain: no Vulkan queue family specified."); return std::nullopt; } if (queueFamilyIndices.size() == 1) { swapChainCi.mCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapChainCi.setQueueFamilyIndices({}); } else { swapChainCi.mCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; swapChainCi.setQueueFamilyIndices( std::vector(queueFamilyIndices.begin(), queueFamilyIndices.end())); } return std::optional(swapChainCi); } VkFormat SwapChainStateVk::getFormat() { return k_vkFormat; } VkExtent2D SwapChainStateVk::getImageExtent() const { return m_vkImageExtent; } const std::vector& SwapChainStateVk::getVkImages() const { return m_vkImages; } const std::vector& SwapChainStateVk::getVkImageViews() const { return m_vkImageViews; } VkSwapchainKHR SwapChainStateVk::getSwapChain() const { return m_vkSwapChain; } } // namespace vk } // namespace gfxstream