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