1 #include "SwapChainStateVk.h"
2 
3 #include <cinttypes>
4 #include <unordered_set>
5 
6 #include "host-common/GfxstreamFatalError.h"
7 #include "host-common/logging.h"
8 #include "vulkan/vk_enum_string_helper.h"
9 #include "vulkan/vk_util.h"
10 
11 namespace gfxstream {
12 namespace vk {
13 
14 using emugl::ABORT_REASON_OTHER;
15 using emugl::FatalError;
16 
17 namespace {
18 
swap(SwapchainCreateInfoWrapper & a,SwapchainCreateInfoWrapper & b)19 void swap(SwapchainCreateInfoWrapper& a, SwapchainCreateInfoWrapper& b) {
20     std::swap(a.mQueueFamilyIndices, b.mQueueFamilyIndices);
21     std::swap(a.mCreateInfo, b.mCreateInfo);
22     // The C++ spec guarantees that after std::swap is called, all iterators and references of the
23     // container remain valid, and the past-the-end iterator is invalidated. Therefore, no need to
24     // reset the VkSwapchainCreateInfoKHR::pQueueFamilyIndices.
25 }
26 
27 }  // namespace
28 
SwapchainCreateInfoWrapper(const VkSwapchainCreateInfoKHR & createInfo)29 SwapchainCreateInfoWrapper::SwapchainCreateInfoWrapper(const VkSwapchainCreateInfoKHR& createInfo)
30     : mCreateInfo(createInfo) {
31     if (createInfo.pNext) {
32         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
33             << "VkSwapchainCreateInfoKHR with pNext in the chain is not supported.";
34     }
35 
36     if (createInfo.pQueueFamilyIndices && (createInfo.queueFamilyIndexCount > 0)) {
37         setQueueFamilyIndices(std::vector<uint32_t>(
38             createInfo.pQueueFamilyIndices,
39             createInfo.pQueueFamilyIndices + createInfo.queueFamilyIndexCount));
40     } else {
41         setQueueFamilyIndices({});
42     }
43 }
44 
SwapchainCreateInfoWrapper(const SwapchainCreateInfoWrapper & other)45 SwapchainCreateInfoWrapper::SwapchainCreateInfoWrapper(const SwapchainCreateInfoWrapper& other)
46     : mCreateInfo(other.mCreateInfo) {
47     if (other.mCreateInfo.pNext) {
48         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
49             << "VkSwapchainCreateInfoKHR with pNext in the chain is not supported.";
50     }
51     setQueueFamilyIndices(other.mQueueFamilyIndices);
52 }
53 
operator =(const SwapchainCreateInfoWrapper & other)54 SwapchainCreateInfoWrapper& SwapchainCreateInfoWrapper::operator=(
55     const SwapchainCreateInfoWrapper& other) {
56     SwapchainCreateInfoWrapper tmp(other);
57     swap(*this, tmp);
58     return *this;
59 }
60 
setQueueFamilyIndices(const std::vector<uint32_t> & queueFamilyIndices)61 void SwapchainCreateInfoWrapper::setQueueFamilyIndices(
62     const std::vector<uint32_t>& queueFamilyIndices) {
63     mQueueFamilyIndices = queueFamilyIndices;
64     mCreateInfo.queueFamilyIndexCount = static_cast<uint32_t>(mQueueFamilyIndices.size());
65     if (mQueueFamilyIndices.empty()) {
66         mCreateInfo.pQueueFamilyIndices = nullptr;
67     } else {
68         mCreateInfo.pQueueFamilyIndices = queueFamilyIndices.data();
69     }
70 }
71 
createSwapChainVk(const VulkanDispatch & vk,VkDevice vkDevice,const VkSwapchainCreateInfoKHR & swapChainCi)72 std::unique_ptr<SwapChainStateVk> SwapChainStateVk::createSwapChainVk(
73     const VulkanDispatch& vk, VkDevice vkDevice, const VkSwapchainCreateInfoKHR& swapChainCi) {
74     std::unique_ptr<SwapChainStateVk> swapChainVk(new SwapChainStateVk(vk, vkDevice));
75     if (swapChainVk->initSwapChainStateVk(swapChainCi) != VK_SUCCESS) {
76         return nullptr;
77     }
78     return swapChainVk;
79 }
80 
SwapChainStateVk(const VulkanDispatch & vk,VkDevice vkDevice)81 SwapChainStateVk::SwapChainStateVk(const VulkanDispatch& vk, VkDevice vkDevice)
82     : m_vk(vk),
83       m_vkDevice(vkDevice),
84       m_vkSwapChain(VK_NULL_HANDLE),
85       m_vkImages(0),
86       m_vkImageViews(0) {}
87 
initSwapChainStateVk(const VkSwapchainCreateInfoKHR & swapChainCi)88 VkResult SwapChainStateVk::initSwapChainStateVk(const VkSwapchainCreateInfoKHR& swapChainCi) {
89     VkResult res = m_vk.vkCreateSwapchainKHR(m_vkDevice, &swapChainCi, nullptr, &m_vkSwapChain);
90     if (res == VK_ERROR_INITIALIZATION_FAILED) return res;
91     VK_CHECK(res);
92     uint32_t imageCount = 0;
93     VK_CHECK(m_vk.vkGetSwapchainImagesKHR(m_vkDevice, m_vkSwapChain, &imageCount, nullptr));
94     m_vkImageExtent = swapChainCi.imageExtent;
95     m_vkImages.resize(imageCount);
96     VK_CHECK(
97         m_vk.vkGetSwapchainImagesKHR(m_vkDevice, m_vkSwapChain, &imageCount, m_vkImages.data()));
98     for (auto i = 0; i < m_vkImages.size(); i++) {
99         VkImageViewCreateInfo imageViewCi = {
100             .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
101             .image = m_vkImages[i],
102             .viewType = VK_IMAGE_VIEW_TYPE_2D,
103             .format = k_vkFormat,
104             .components = {.r = VK_COMPONENT_SWIZZLE_IDENTITY,
105                            .g = VK_COMPONENT_SWIZZLE_IDENTITY,
106                            .b = VK_COMPONENT_SWIZZLE_IDENTITY,
107                            .a = VK_COMPONENT_SWIZZLE_IDENTITY},
108             .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
109                                  .baseMipLevel = 0,
110                                  .levelCount = 1,
111                                  .baseArrayLayer = 0,
112                                  .layerCount = 1}};
113         VkImageView vkImageView;
114         VK_CHECK(m_vk.vkCreateImageView(m_vkDevice, &imageViewCi, nullptr, &vkImageView));
115         m_vkImageViews.push_back(vkImageView);
116     }
117     return VK_SUCCESS;
118 }
119 
~SwapChainStateVk()120 SwapChainStateVk::~SwapChainStateVk() {
121     for (auto imageView : m_vkImageViews) {
122         m_vk.vkDestroyImageView(m_vkDevice, imageView, nullptr);
123     }
124     if (m_vkSwapChain != VK_NULL_HANDLE) {
125         m_vk.vkDestroySwapchainKHR(m_vkDevice, m_vkSwapChain, nullptr);
126     }
127 }
128 
getRequiredInstanceExtensions()129 std::vector<const char*> SwapChainStateVk::getRequiredInstanceExtensions() {
130     return {
131             VK_KHR_SURFACE_EXTENSION_NAME,
132 #ifdef _WIN32
133             VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
134 #endif
135 #ifdef __APPLE__
136             VK_EXT_METAL_SURFACE_EXTENSION_NAME,
137 #endif
138 #ifdef VK_USE_PLATFORM_XCB_KHR
139             VK_KHR_XCB_SURFACE_EXTENSION_NAME,
140 #endif
141     };
142 }
143 
getRequiredDeviceExtensions()144 std::vector<const char*> SwapChainStateVk::getRequiredDeviceExtensions() {
145     return {
146         VK_KHR_SWAPCHAIN_EXTENSION_NAME,
147     };
148 }
149 
validateQueueFamilyProperties(const VulkanDispatch & vk,VkPhysicalDevice physicalDevice,VkSurfaceKHR surface,uint32_t queueFamilyIndex)150 bool SwapChainStateVk::validateQueueFamilyProperties(const VulkanDispatch& vk,
151                                                      VkPhysicalDevice physicalDevice,
152                                                      VkSurfaceKHR surface,
153                                                      uint32_t queueFamilyIndex) {
154     VkBool32 presentSupport = VK_FALSE;
155     VK_CHECK(vk.vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface,
156                                                      &presentSupport));
157     return presentSupport;
158 }
159 
createSwapChainCi(const VulkanDispatch & vk,VkSurfaceKHR surface,VkPhysicalDevice physicalDevice,uint32_t width,uint32_t height,const std::unordered_set<uint32_t> & queueFamilyIndices)160 std::optional<SwapchainCreateInfoWrapper> SwapChainStateVk::createSwapChainCi(
161     const VulkanDispatch& vk, VkSurfaceKHR surface, VkPhysicalDevice physicalDevice, uint32_t width,
162     uint32_t height, const std::unordered_set<uint32_t>& queueFamilyIndices) {
163     uint32_t formatCount = 0;
164     VK_CHECK(
165         vk.vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr));
166     std::vector<VkSurfaceFormatKHR> formats(formatCount);
167     VkResult res = vk.vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount,
168                                                            formats.data());
169     // b/217226027: drivers may return VK_INCOMPLETE with pSurfaceFormatCount returned by
170     // vkGetPhysicalDeviceSurfaceFormatsKHR. Retry here as a work around to the potential driver
171     // bug.
172     if (res == VK_INCOMPLETE) {
173         formatCount = (formatCount + 1) * 2;
174         INFO(
175             "VK_INCOMPLETE returned by vkGetPhysicalDeviceSurfaceFormatsKHR. A possible driver "
176             "bug. Retry with *pSurfaceFormatCount = %" PRIu32 ".",
177             formatCount);
178         formats.resize(formatCount);
179         res = vk.vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount,
180                                                       formats.data());
181         formats.resize(formatCount);
182     }
183     if (res == VK_INCOMPLETE) {
184         INFO(
185             "VK_INCOMPLETE still returned by vkGetPhysicalDeviceSurfaceFormatsKHR with retry. A "
186             "possible driver bug.");
187     } else {
188         VK_CHECK(res);
189     }
190     auto iSurfaceFormat =
191         std::find_if(formats.begin(), formats.end(), [](const VkSurfaceFormatKHR& format) {
192             return format.format == k_vkFormat && format.colorSpace == k_vkColorSpace;
193         });
194     if (iSurfaceFormat == formats.end()) {
195         ERR("Failed to create swapchain: the format(%#" PRIx64
196                                 ") with color space(%#" PRIx64 ") not supported.",
197                                 static_cast<uint64_t>(k_vkFormat),
198                                 static_cast<uint64_t>(k_vkColorSpace));
199         return std::nullopt;
200     }
201 
202     uint32_t presentModeCount = 0;
203     VK_CHECK(vk.vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface,
204                                                           &presentModeCount, nullptr));
205     std::vector<VkPresentModeKHR> presentModes_(presentModeCount);
206     VK_CHECK(vk.vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface,
207                                                           &presentModeCount, presentModes_.data()));
208     std::unordered_set<VkPresentModeKHR> presentModes(presentModes_.begin(), presentModes_.end());
209     VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
210     if (!presentModes.count(VK_PRESENT_MODE_FIFO_KHR)) {
211         ERR("Failed to create swapchain: FIFO present mode not supported.");
212         return std::nullopt;
213     }
214     VkFormatProperties formatProperties = {};
215     vk.vkGetPhysicalDeviceFormatProperties(physicalDevice, k_vkFormat, &formatProperties);
216     // According to the spec, a presentable image is equivalent to a non-presentable image created
217     // with the VK_IMAGE_TILING_OPTIMAL tiling parameter.
218     VkFormatFeatureFlags formatFeatures = formatProperties.optimalTilingFeatures;
219     if (!(formatFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) {
220         // According to VUID-vkCmdBlitImage-dstImage-02000, the format features of dstImage must
221         // contain VK_FORMAT_FEATURE_BLIT_DST_BIT.
222         ERR(
223             "The format %s with the optimal tiling doesn't support VK_FORMAT_FEATURE_BLIT_DST_BIT. "
224             "The supported features are %s.",
225             string_VkFormat(k_vkFormat), string_VkFormatFeatureFlags(formatFeatures).c_str());
226         return std::nullopt;
227     }
228     VkSurfaceCapabilitiesKHR surfaceCaps;
229     VK_CHECK(vk.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCaps));
230     if (!(surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) {
231         ERR(
232             "The supported usage flags of the presentable images is %s, and don't contain "
233             "VK_IMAGE_USAGE_TRANSFER_DST_BIT.",
234             string_VkImageUsageFlags(surfaceCaps.supportedUsageFlags).c_str());
235         return std::nullopt;
236     }
237     std::optional<VkExtent2D> maybeExtent = std::nullopt;
238     if (surfaceCaps.currentExtent.width != UINT32_MAX && surfaceCaps.currentExtent.width == width &&
239         surfaceCaps.currentExtent.height == height) {
240         maybeExtent = surfaceCaps.currentExtent;
241     } else if (width >= surfaceCaps.minImageExtent.width &&
242                width <= surfaceCaps.maxImageExtent.width &&
243                height >= surfaceCaps.minImageExtent.height &&
244                height <= surfaceCaps.maxImageExtent.height) {
245         maybeExtent = VkExtent2D({width, height});
246     }
247     if (!maybeExtent.has_value()) {
248         ERR("Failed to create swapchain: extent(%" PRIu64 "x%" PRIu64
249                                 ") not supported.",
250                                 static_cast<uint64_t>(width), static_cast<uint64_t>(height));
251         return std::nullopt;
252     }
253     auto extent = maybeExtent.value();
254     uint32_t imageCount = surfaceCaps.minImageCount + 1;
255     if (surfaceCaps.maxImageCount != 0 && surfaceCaps.maxImageCount < imageCount) {
256         imageCount = surfaceCaps.maxImageCount;
257     }
258     SwapchainCreateInfoWrapper swapChainCi(VkSwapchainCreateInfoKHR{
259         .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
260         .pNext = nullptr,
261         .flags = VkSwapchainCreateFlagsKHR{0},
262         .surface = surface,
263         .minImageCount = imageCount,
264         .imageFormat = iSurfaceFormat->format,
265         .imageColorSpace = iSurfaceFormat->colorSpace,
266         .imageExtent = extent,
267         .imageArrayLayers = 1,
268         .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
269         .imageSharingMode = VkSharingMode{},
270         .queueFamilyIndexCount = 0,
271         .pQueueFamilyIndices = nullptr,
272         .preTransform = surfaceCaps.currentTransform,
273         .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
274         .presentMode = presentMode,
275         .clipped = VK_TRUE,
276         .oldSwapchain = VK_NULL_HANDLE});
277     if (queueFamilyIndices.empty()) {
278         ERR("Failed to create swapchain: no Vulkan queue family specified.");
279         return std::nullopt;
280     }
281     if (queueFamilyIndices.size() == 1) {
282         swapChainCi.mCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
283         swapChainCi.setQueueFamilyIndices({});
284     } else {
285         swapChainCi.mCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
286         swapChainCi.setQueueFamilyIndices(
287             std::vector<uint32_t>(queueFamilyIndices.begin(), queueFamilyIndices.end()));
288     }
289     return std::optional(swapChainCi);
290 }
291 
getFormat()292 VkFormat SwapChainStateVk::getFormat() { return k_vkFormat; }
293 
getImageExtent() const294 VkExtent2D SwapChainStateVk::getImageExtent() const { return m_vkImageExtent; }
295 
getVkImages() const296 const std::vector<VkImage>& SwapChainStateVk::getVkImages() const { return m_vkImages; }
297 
getVkImageViews() const298 const std::vector<VkImageView>& SwapChainStateVk::getVkImageViews() const { return m_vkImageViews; }
299 
getSwapChain() const300 VkSwapchainKHR SwapChainStateVk::getSwapChain() const { return m_vkSwapChain; }
301 
302 }  // namespace vk
303 }  // namespace gfxstream
304