1 /*
2 * Copyright 2023 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "tools/window/VulkanWindowContext.h"
9
10 #include "include/core/SkSurface.h"
11 #include "include/gpu/ganesh/GrBackendSemaphore.h"
12 #include "include/gpu/ganesh/GrBackendSurface.h"
13 #include "include/gpu/ganesh/GrDirectContext.h"
14 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
15 #include "include/gpu/ganesh/vk/GrVkBackendSemaphore.h"
16 #include "include/gpu/ganesh/vk/GrVkBackendSurface.h"
17 #include "include/gpu/ganesh/vk/GrVkDirectContext.h"
18 #include "include/gpu/ganesh/vk/GrVkTypes.h"
19 #include "include/gpu/vk/VulkanExtensions.h"
20 #include "src/gpu/GpuTypesPriv.h"
21 #include "include/gpu/vk/VulkanMutableTextureState.h"
22 #include "src/base/SkAutoMalloc.h"
23 #include "src/gpu/ganesh/vk/GrVkImage.h"
24 #include "src/gpu/ganesh/vk/GrVkUtil.h"
25 #include "src/gpu/vk/VulkanInterface.h"
26 #include "src/gpu/vk/vulkanmemoryallocator/VulkanAMDMemoryAllocator.h"
27
28 #ifdef VK_USE_PLATFORM_WIN32_KHR
29 // windows wants to define this as CreateSemaphoreA or CreateSemaphoreW
30 #undef CreateSemaphore
31 #endif
32
33 #define GET_PROC(F) f ## F = \
34 (PFN_vk ## F) backendContext.fGetProc("vk" #F, fInstance, VK_NULL_HANDLE)
35 #define GET_DEV_PROC(F) f ## F = \
36 (PFN_vk ## F) backendContext.fGetProc("vk" #F, VK_NULL_HANDLE, fDevice)
37
38 namespace skwindow::internal {
39
VulkanWindowContext(std::unique_ptr<const DisplayParams> params,CreateVkSurfaceFn createVkSurface,CanPresentFn canPresent,PFN_vkGetInstanceProcAddr instProc)40 VulkanWindowContext::VulkanWindowContext(std::unique_ptr<const DisplayParams> params,
41 CreateVkSurfaceFn createVkSurface,
42 CanPresentFn canPresent,
43 PFN_vkGetInstanceProcAddr instProc)
44 : WindowContext(std::move(params))
45 , fCreateVkSurfaceFn(std::move(createVkSurface))
46 , fCanPresentFn(std::move(canPresent))
47 , fSurface(VK_NULL_HANDLE)
48 , fSwapchain(VK_NULL_HANDLE)
49 , fImages(nullptr)
50 , fImageLayouts(nullptr)
51 , fSurfaces(nullptr)
52 , fBackbuffers(nullptr) {
53 fGetInstanceProcAddr = instProc;
54 this->initializeContext();
55 }
56
initializeContext()57 void VulkanWindowContext::initializeContext() {
58 SkASSERT(!fContext);
59 // any config code here (particularly for msaa)?
60
61 PFN_vkGetInstanceProcAddr getInstanceProc = fGetInstanceProcAddr;
62 skgpu::VulkanBackendContext backendContext;
63 skgpu::VulkanExtensions extensions;
64 VkPhysicalDeviceFeatures2 features;
65 if (!sk_gpu_test::CreateVkBackendContext(getInstanceProc,
66 &backendContext,
67 &extensions,
68 &features,
69 &fDebugCallback,
70 &fPresentQueueIndex,
71 fCanPresentFn,
72 fDisplayParams->createProtectedNativeBackend())) {
73 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
74 return;
75 }
76
77 if (!extensions.hasExtension(VK_KHR_SURFACE_EXTENSION_NAME, 25) ||
78 !extensions.hasExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, 68)) {
79 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
80 return;
81 }
82
83 fInstance = backendContext.fInstance;
84 fPhysicalDevice = backendContext.fPhysicalDevice;
85 fDevice = backendContext.fDevice;
86 fGraphicsQueueIndex = backendContext.fGraphicsQueueIndex;
87 fGraphicsQueue = backendContext.fQueue;
88
89 PFN_vkGetPhysicalDeviceProperties localGetPhysicalDeviceProperties =
90 reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(
91 backendContext.fGetProc("vkGetPhysicalDeviceProperties",
92 backendContext.fInstance,
93 VK_NULL_HANDLE));
94 if (!localGetPhysicalDeviceProperties) {
95 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
96 return;
97 }
98 VkPhysicalDeviceProperties physDeviceProperties;
99 localGetPhysicalDeviceProperties(backendContext.fPhysicalDevice, &physDeviceProperties);
100 uint32_t physDevVersion = physDeviceProperties.apiVersion;
101
102 fInterface.reset(new skgpu::VulkanInterface(backendContext.fGetProc,
103 fInstance,
104 fDevice,
105 backendContext.fMaxAPIVersion,
106 physDevVersion,
107 &extensions));
108
109 GET_PROC(DestroyInstance);
110 if (fDebugCallback != VK_NULL_HANDLE) {
111 GET_PROC(DestroyDebugReportCallbackEXT);
112 }
113 GET_PROC(DestroySurfaceKHR);
114 GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
115 GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
116 GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
117 GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
118 GET_DEV_PROC(DeviceWaitIdle);
119 GET_DEV_PROC(QueueWaitIdle);
120 GET_DEV_PROC(DestroyDevice);
121 GET_DEV_PROC(CreateSwapchainKHR);
122 GET_DEV_PROC(DestroySwapchainKHR);
123 GET_DEV_PROC(GetSwapchainImagesKHR);
124 GET_DEV_PROC(AcquireNextImageKHR);
125 GET_DEV_PROC(QueuePresentKHR);
126 GET_DEV_PROC(GetDeviceQueue);
127
128 backendContext.fMemoryAllocator =
129 skgpu::VulkanAMDMemoryAllocator::Make(fInstance,
130 backendContext.fPhysicalDevice,
131 backendContext.fDevice,
132 physDevVersion,
133 &extensions,
134 fInterface.get(),
135 skgpu::ThreadSafe::kNo,
136 /*blockSize=*/std::nullopt);
137
138 fContext = GrDirectContexts::MakeVulkan(backendContext, fDisplayParams->grContextOptions());
139
140 fSurface = fCreateVkSurfaceFn(fInstance);
141 if (VK_NULL_HANDLE == fSurface) {
142 this->destroyContext();
143 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
144 return;
145 }
146
147 VkBool32 supported;
148 VkResult res = fGetPhysicalDeviceSurfaceSupportKHR(fPhysicalDevice, fPresentQueueIndex,
149 fSurface, &supported);
150 if (VK_SUCCESS != res) {
151 this->destroyContext();
152 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
153 return;
154 }
155
156 if (!this->createSwapchain(-1, -1)) {
157 this->destroyContext();
158 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
159 return;
160 }
161
162 // create presentQueue
163 fGetDeviceQueue(fDevice, fPresentQueueIndex, 0, &fPresentQueue);
164 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
165 }
166
createSwapchain(int width,int height)167 bool VulkanWindowContext::createSwapchain(int width, int height) {
168 // check for capabilities
169 VkSurfaceCapabilitiesKHR caps;
170 VkResult res = fGetPhysicalDeviceSurfaceCapabilitiesKHR(fPhysicalDevice, fSurface, &caps);
171 if (VK_SUCCESS != res) {
172 return false;
173 }
174
175 uint32_t surfaceFormatCount;
176 res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
177 nullptr);
178 if (VK_SUCCESS != res) {
179 return false;
180 }
181
182 SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR));
183 VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get();
184 res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
185 surfaceFormats);
186 if (VK_SUCCESS != res) {
187 return false;
188 }
189
190 uint32_t presentModeCount;
191 res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
192 nullptr);
193 if (VK_SUCCESS != res) {
194 return false;
195 }
196
197 SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR));
198 VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get();
199 res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
200 presentModes);
201 if (VK_SUCCESS != res) {
202 return false;
203 }
204
205 VkExtent2D extent = caps.currentExtent;
206 // use the hints
207 if (extent.width == (uint32_t)-1) {
208 extent.width = width;
209 extent.height = height;
210 }
211
212 // clamp width; to protect us from broken hints
213 if (extent.width < caps.minImageExtent.width) {
214 extent.width = caps.minImageExtent.width;
215 } else if (extent.width > caps.maxImageExtent.width) {
216 extent.width = caps.maxImageExtent.width;
217 }
218 // clamp height
219 if (extent.height < caps.minImageExtent.height) {
220 extent.height = caps.minImageExtent.height;
221 } else if (extent.height > caps.maxImageExtent.height) {
222 extent.height = caps.maxImageExtent.height;
223 }
224
225 fWidth = (int)extent.width;
226 fHeight = (int)extent.height;
227
228 uint32_t imageCount = caps.minImageCount + 2;
229 if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
230 // Application must settle for fewer images than desired:
231 imageCount = caps.maxImageCount;
232 }
233
234 VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
235 VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
236 VK_IMAGE_USAGE_TRANSFER_DST_BIT;
237 SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags);
238 if (caps.supportedUsageFlags & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) {
239 usageFlags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
240 }
241 if (caps.supportedUsageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) {
242 usageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
243 }
244 SkASSERT(caps.supportedTransforms & caps.currentTransform);
245 SkASSERT(caps.supportedCompositeAlpha & (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
246 VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
247 VkCompositeAlphaFlagBitsKHR composite_alpha =
248 (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ?
249 VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR :
250 VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
251
252 // Pick our surface format.
253 VkFormat surfaceFormat = VK_FORMAT_UNDEFINED;
254 VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
255 for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
256 VkFormat localFormat = surfaceFormats[i].format;
257 if (GrVkFormatIsSupported(localFormat)) {
258 surfaceFormat = localFormat;
259 colorSpace = surfaceFormats[i].colorSpace;
260 break;
261 }
262 }
263 fSampleCount = std::max(1, fDisplayParams->msaaSampleCount());
264 fStencilBits = 8;
265
266 if (VK_FORMAT_UNDEFINED == surfaceFormat) {
267 return false;
268 }
269
270 SkColorType colorType;
271 switch (surfaceFormat) {
272 case VK_FORMAT_R8G8B8A8_UNORM: // fall through
273 case VK_FORMAT_R8G8B8A8_SRGB:
274 colorType = kRGBA_8888_SkColorType;
275 break;
276 case VK_FORMAT_B8G8R8A8_UNORM: // fall through
277 colorType = kBGRA_8888_SkColorType;
278 break;
279 default:
280 return false;
281 }
282
283 // If mailbox mode is available, use it, as it is the lowest-latency non-
284 // tearing mode. If not, fall back to FIFO which is always available.
285 VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
286 bool hasImmediate = false;
287 for (uint32_t i = 0; i < presentModeCount; ++i) {
288 // use mailbox
289 if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) {
290 mode = VK_PRESENT_MODE_MAILBOX_KHR;
291 }
292 if (VK_PRESENT_MODE_IMMEDIATE_KHR == presentModes[i]) {
293 hasImmediate = true;
294 }
295 }
296 if (fDisplayParams->disableVsync() && hasImmediate) {
297 mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
298 }
299
300 VkSwapchainCreateInfoKHR swapchainCreateInfo;
301 memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
302 swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
303 swapchainCreateInfo.flags = fDisplayParams->createProtectedNativeBackend()
304 ? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR
305 : 0;
306 swapchainCreateInfo.surface = fSurface;
307 swapchainCreateInfo.minImageCount = imageCount;
308 swapchainCreateInfo.imageFormat = surfaceFormat;
309 swapchainCreateInfo.imageColorSpace = colorSpace;
310 swapchainCreateInfo.imageExtent = extent;
311 swapchainCreateInfo.imageArrayLayers = 1;
312 swapchainCreateInfo.imageUsage = usageFlags;
313
314 uint32_t queueFamilies[] = { fGraphicsQueueIndex, fPresentQueueIndex };
315 if (fGraphicsQueueIndex != fPresentQueueIndex) {
316 swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
317 swapchainCreateInfo.queueFamilyIndexCount = 2;
318 swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
319 } else {
320 swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
321 swapchainCreateInfo.queueFamilyIndexCount = 0;
322 swapchainCreateInfo.pQueueFamilyIndices = nullptr;
323 }
324
325 swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
326 swapchainCreateInfo.compositeAlpha = composite_alpha;
327 swapchainCreateInfo.presentMode = mode;
328 swapchainCreateInfo.clipped = true;
329 swapchainCreateInfo.oldSwapchain = fSwapchain;
330
331 res = fCreateSwapchainKHR(fDevice, &swapchainCreateInfo, nullptr, &fSwapchain);
332 if (VK_SUCCESS != res) {
333 return false;
334 }
335
336 // destroy the old swapchain
337 if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
338 fDeviceWaitIdle(fDevice);
339
340 this->destroyBuffers();
341
342 fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
343 }
344
345 if (!this->createBuffers(swapchainCreateInfo.imageFormat, usageFlags, colorType,
346 swapchainCreateInfo.imageSharingMode)) {
347 fDeviceWaitIdle(fDevice);
348
349 this->destroyBuffers();
350
351 fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
352 }
353
354 return true;
355 }
356
createBuffers(VkFormat format,VkImageUsageFlags usageFlags,SkColorType colorType,VkSharingMode sharingMode)357 bool VulkanWindowContext::createBuffers(VkFormat format,
358 VkImageUsageFlags usageFlags,
359 SkColorType colorType,
360 VkSharingMode sharingMode) {
361 fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, nullptr);
362 SkASSERT(fImageCount);
363 fImages = new VkImage[fImageCount];
364 fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, fImages);
365
366 // set up initial image layouts and create surfaces
367 fImageLayouts = new VkImageLayout[fImageCount];
368 fSurfaces = new sk_sp<SkSurface>[fImageCount];
369 for (uint32_t i = 0; i < fImageCount; ++i) {
370 fImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
371
372 GrVkImageInfo info;
373 info.fImage = fImages[i];
374 info.fAlloc = skgpu::VulkanAlloc();
375 info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
376 info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
377 info.fFormat = format;
378 info.fImageUsageFlags = usageFlags;
379 info.fLevelCount = 1;
380 info.fCurrentQueueFamily = fPresentQueueIndex;
381 info.fProtected = skgpu::Protected(fDisplayParams->createProtectedNativeBackend());
382 info.fSharingMode = sharingMode;
383
384 if (usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) {
385 GrBackendTexture backendTexture = GrBackendTextures::MakeVk(fWidth, fHeight, info);
386 fSurfaces[i] = SkSurfaces::WrapBackendTexture(fContext.get(),
387 backendTexture,
388 kTopLeft_GrSurfaceOrigin,
389 fDisplayParams->msaaSampleCount(),
390 colorType,
391 fDisplayParams->colorSpace(),
392 &fDisplayParams->surfaceProps());
393 } else {
394 if (fDisplayParams->msaaSampleCount() > 1) {
395 return false;
396 }
397 info.fSampleCount = fSampleCount;
398 GrBackendRenderTarget backendRT = GrBackendRenderTargets::MakeVk(fWidth, fHeight, info);
399 fSurfaces[i] = SkSurfaces::WrapBackendRenderTarget(fContext.get(),
400 backendRT,
401 kTopLeft_GrSurfaceOrigin,
402 colorType,
403 fDisplayParams->colorSpace(),
404 &fDisplayParams->surfaceProps());
405 }
406 if (!fSurfaces[i]) {
407 return false;
408 }
409 }
410
411 // set up the backbuffers
412 VkSemaphoreCreateInfo semaphoreInfo;
413 memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
414 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
415 semaphoreInfo.pNext = nullptr;
416 semaphoreInfo.flags = 0;
417
418 // we create one additional backbuffer structure here, because we want to
419 // give the command buffers they contain a chance to finish before we cycle back
420 fBackbuffers = new BackbufferInfo[fImageCount + 1];
421 for (uint32_t i = 0; i < fImageCount + 1; ++i) {
422 fBackbuffers[i].fImageIndex = -1;
423 SkDEBUGCODE(VkResult result = )GR_VK_CALL(fInterface,
424 CreateSemaphore(fDevice, &semaphoreInfo, nullptr,
425 &fBackbuffers[i].fRenderSemaphore));
426 SkASSERT(result == VK_SUCCESS);
427 }
428 fCurrentBackbufferIndex = fImageCount;
429 return true;
430 }
431
destroyBuffers()432 void VulkanWindowContext::destroyBuffers() {
433
434 if (fBackbuffers) {
435 for (uint32_t i = 0; i < fImageCount + 1; ++i) {
436 fBackbuffers[i].fImageIndex = -1;
437 GR_VK_CALL(fInterface,
438 DestroySemaphore(fDevice,
439 fBackbuffers[i].fRenderSemaphore,
440 nullptr));
441 }
442 }
443
444 delete[] fBackbuffers;
445 fBackbuffers = nullptr;
446
447 // Does this actually free the surfaces?
448 delete[] fSurfaces;
449 fSurfaces = nullptr;
450 delete[] fImageLayouts;
451 fImageLayouts = nullptr;
452 delete[] fImages;
453 fImages = nullptr;
454 }
455
~VulkanWindowContext()456 VulkanWindowContext::~VulkanWindowContext() {
457 this->destroyContext();
458 }
459
destroyContext()460 void VulkanWindowContext::destroyContext() {
461 if (this->isValid()) {
462 fQueueWaitIdle(fPresentQueue);
463 fDeviceWaitIdle(fDevice);
464
465 this->destroyBuffers();
466
467 if (VK_NULL_HANDLE != fSwapchain) {
468 fDestroySwapchainKHR(fDevice, fSwapchain, nullptr);
469 fSwapchain = VK_NULL_HANDLE;
470 }
471
472 if (VK_NULL_HANDLE != fSurface) {
473 fDestroySurfaceKHR(fInstance, fSurface, nullptr);
474 fSurface = VK_NULL_HANDLE;
475 }
476 }
477
478 SkASSERT(fContext->unique());
479 fContext.reset();
480 fInterface.reset();
481
482 if (VK_NULL_HANDLE != fDevice) {
483 fDestroyDevice(fDevice, nullptr);
484 fDevice = VK_NULL_HANDLE;
485 }
486
487 #ifdef SK_ENABLE_VK_LAYERS
488 if (fDebugCallback != VK_NULL_HANDLE) {
489 fDestroyDebugReportCallbackEXT(fInstance, fDebugCallback, nullptr);
490 }
491 #endif
492
493 fPhysicalDevice = VK_NULL_HANDLE;
494
495 if (VK_NULL_HANDLE != fInstance) {
496 fDestroyInstance(fInstance, nullptr);
497 fInstance = VK_NULL_HANDLE;
498 }
499 }
500
getAvailableBackbuffer()501 VulkanWindowContext::BackbufferInfo* VulkanWindowContext::getAvailableBackbuffer() {
502 SkASSERT(fBackbuffers);
503
504 ++fCurrentBackbufferIndex;
505 if (fCurrentBackbufferIndex > fImageCount) {
506 fCurrentBackbufferIndex = 0;
507 }
508
509 BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
510 return backbuffer;
511 }
512
getBackbufferSurface()513 sk_sp<SkSurface> VulkanWindowContext::getBackbufferSurface() {
514 BackbufferInfo* backbuffer = this->getAvailableBackbuffer();
515 SkASSERT(backbuffer);
516
517 // semaphores should be in unsignaled state
518 VkSemaphoreCreateInfo semaphoreInfo;
519 memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
520 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
521 semaphoreInfo.pNext = nullptr;
522 semaphoreInfo.flags = 0;
523 VkSemaphore semaphore;
524 SkDEBUGCODE(VkResult result = )GR_VK_CALL(fInterface, CreateSemaphore(fDevice, &semaphoreInfo,
525 nullptr, &semaphore));
526 SkASSERT(result == VK_SUCCESS);
527
528 // acquire the image
529 VkResult res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
530 semaphore, VK_NULL_HANDLE,
531 &backbuffer->fImageIndex);
532 if (VK_ERROR_SURFACE_LOST_KHR == res) {
533 // need to figure out how to create a new vkSurface without the platformData*
534 // maybe use attach somehow? but need a Window
535 GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
536 return nullptr;
537 }
538 if (VK_ERROR_OUT_OF_DATE_KHR == res) {
539 // tear swapchain down and try again
540 if (!this->createSwapchain(-1, -1)) {
541 GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
542 return nullptr;
543 }
544 backbuffer = this->getAvailableBackbuffer();
545
546 // acquire the image
547 res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
548 semaphore, VK_NULL_HANDLE,
549 &backbuffer->fImageIndex);
550
551 if (VK_SUCCESS != res) {
552 GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
553 return nullptr;
554 }
555 }
556
557 SkSurface* surface = fSurfaces[backbuffer->fImageIndex].get();
558
559 GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(semaphore);
560
561 surface->wait(1, &beSemaphore);
562
563 return sk_ref_sp(surface);
564 }
565
onSwapBuffers()566 void VulkanWindowContext::onSwapBuffers() {
567
568 BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
569 SkSurface* surface = fSurfaces[backbuffer->fImageIndex].get();
570
571 GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(backbuffer->fRenderSemaphore);
572
573 GrFlushInfo info;
574 info.fNumSemaphores = 1;
575 info.fSignalSemaphores = &beSemaphore;
576 skgpu::MutableTextureState presentState = skgpu::MutableTextureStates::MakeVulkan(
577 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, fPresentQueueIndex);
578 auto dContext = surface->recordingContext()->asDirectContext();
579 dContext->flush(surface, info, &presentState);
580 dContext->submit();
581
582 // Submit present operation to present queue
583 const VkPresentInfoKHR presentInfo =
584 {
585 VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, // sType
586 nullptr, // pNext
587 1, // waitSemaphoreCount
588 &backbuffer->fRenderSemaphore, // pWaitSemaphores
589 1, // swapchainCount
590 &fSwapchain, // pSwapchains
591 &backbuffer->fImageIndex, // pImageIndices
592 nullptr // pResults
593 };
594
595 fQueuePresentKHR(fPresentQueue, &presentInfo);
596 }
597
598 } // namespace skwindow::internal
599