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