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