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