xref: /aosp_15_r20/external/skia/tools/window/VulkanWindowContext.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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