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