• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google Inc.
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/GraphiteVulkanWindowContext.h"
9 
10 #include "include/core/SkSurface.h"
11 #include "include/gpu/MutableTextureState.h"
12 #include "include/gpu/graphite/BackendSemaphore.h"
13 #include "include/gpu/graphite/BackendTexture.h"
14 #include "include/gpu/graphite/Context.h"
15 #include "include/gpu/graphite/ContextOptions.h"
16 #include "include/gpu/graphite/GraphiteTypes.h"
17 #include "include/gpu/graphite/Recorder.h"
18 #include "include/gpu/graphite/Surface.h"
19 #include "include/gpu/graphite/TextureInfo.h"
20 #include "include/gpu/graphite/vk/VulkanGraphiteTypes.h"
21 #include "include/gpu/graphite/vk/VulkanGraphiteUtils.h"
22 #include "include/gpu/vk/VulkanExtensions.h"
23 #include "include/gpu/vk/VulkanMutableTextureState.h"
24 #include "include/gpu/vk/VulkanTypes.h"
25 #include "include/private/gpu/graphite/ContextOptionsPriv.h"
26 #include "src/base/SkAutoMalloc.h"
27 #include "src/gpu/graphite/vk/VulkanGraphiteUtilsPriv.h"
28 #include "src/gpu/vk/VulkanInterface.h"
29 #include "tools/GpuToolUtils.h"
30 #include "tools/ToolUtils.h"
31 
32 #ifdef VK_USE_PLATFORM_WIN32_KHR
33 // windows wants to define this as CreateSemaphoreA or CreateSemaphoreW
34 #undef CreateSemaphore
35 #endif
36 
37 #define GET_PROC(F) f ## F = \
38     (PFN_vk ## F) backendContext.fGetProc("vk" #F, fInstance, VK_NULL_HANDLE)
39 #define GET_DEV_PROC(F) f ## F = \
40     (PFN_vk ## F) backendContext.fGetProc("vk" #F, VK_NULL_HANDLE, fDevice)
41 
42 namespace skwindow::internal {
43 
GraphiteVulkanWindowContext(const DisplayParams & params,CreateVkSurfaceFn createVkSurface,CanPresentFn canPresent,PFN_vkGetInstanceProcAddr instProc)44 GraphiteVulkanWindowContext::GraphiteVulkanWindowContext(const DisplayParams& params,
45                                                          CreateVkSurfaceFn createVkSurface,
46                                                          CanPresentFn canPresent,
47                                                          PFN_vkGetInstanceProcAddr instProc)
48     : WindowContext(params)
49     , fCreateVkSurfaceFn(std::move(createVkSurface))
50     , fCanPresentFn(std::move(canPresent))
51     , fSurface(VK_NULL_HANDLE)
52     , fSwapchain(VK_NULL_HANDLE)
53     , fImages(nullptr)
54     , fImageLayouts(nullptr)
55     , fSurfaces(nullptr)
56     , fBackbuffers(nullptr) {
57     fGetInstanceProcAddr = instProc;
58     this->initializeContext();
59 }
60 
initializeContext()61 void GraphiteVulkanWindowContext::initializeContext() {
62     SkASSERT(!fGraphiteContext && !fGraphiteRecorder);
63     // any config code here (particularly for msaa)?
64 
65     PFN_vkGetInstanceProcAddr getInstanceProc = fGetInstanceProcAddr;
66     skgpu::VulkanBackendContext backendContext;
67     skgpu::VulkanExtensions extensions;
68     VkPhysicalDeviceFeatures2 features;
69     if (!sk_gpu_test::CreateVkBackendContext(getInstanceProc, &backendContext, &extensions,
70                                              &features, &fDebugCallback, &fPresentQueueIndex,
71                                              fCanPresentFn,
72                                              fDisplayParams.fCreateProtectedNativeBackend)) {
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, fInstance, fDevice,
103                                                 backendContext.fMaxAPIVersion, physDevVersion,
104                                                 &extensions));
105 
106     GET_PROC(DestroyInstance);
107     if (fDebugCallback != VK_NULL_HANDLE) {
108         GET_PROC(DestroyDebugReportCallbackEXT);
109     }
110     GET_PROC(DestroySurfaceKHR);
111     GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
112     GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
113     GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
114     GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
115     GET_DEV_PROC(DeviceWaitIdle);
116     GET_DEV_PROC(QueueWaitIdle);
117     GET_DEV_PROC(DestroyDevice);
118     GET_DEV_PROC(CreateSwapchainKHR);
119     GET_DEV_PROC(DestroySwapchainKHR);
120     GET_DEV_PROC(GetSwapchainImagesKHR);
121     GET_DEV_PROC(AcquireNextImageKHR);
122     GET_DEV_PROC(QueuePresentKHR);
123     GET_DEV_PROC(GetDeviceQueue);
124 
125     skgpu::graphite::ContextOptions contextOptions;
126     skgpu::graphite::ContextOptionsPriv contextOptionsPriv;
127     // Needed to make synchronous readPixels work
128     contextOptionsPriv.fStoreContextRefInRecorder = true;
129     contextOptions.fOptionsPriv = &contextOptionsPriv;
130     fGraphiteContext = skgpu::graphite::ContextFactory::MakeVulkan(backendContext, contextOptions);
131     fGraphiteRecorder = fGraphiteContext->makeRecorder(ToolUtils::CreateTestingRecorderOptions());
132 
133     fSurface = fCreateVkSurfaceFn(fInstance);
134     if (VK_NULL_HANDLE == fSurface) {
135         this->destroyContext();
136         sk_gpu_test::FreeVulkanFeaturesStructs(&features);
137         return;
138     }
139 
140     VkBool32 supported;
141     VkResult res = fGetPhysicalDeviceSurfaceSupportKHR(fPhysicalDevice, fPresentQueueIndex,
142                                                        fSurface, &supported);
143     if (VK_SUCCESS != res) {
144         this->destroyContext();
145         sk_gpu_test::FreeVulkanFeaturesStructs(&features);
146         return;
147     }
148 
149     if (!this->createSwapchain(-1, -1, fDisplayParams)) {
150         this->destroyContext();
151         sk_gpu_test::FreeVulkanFeaturesStructs(&features);
152         return;
153     }
154 
155     // create presentQueue
156     fGetDeviceQueue(fDevice, fPresentQueueIndex, 0, &fPresentQueue);
157     sk_gpu_test::FreeVulkanFeaturesStructs(&features);
158 }
159 
createSwapchain(int width,int height,const DisplayParams & params)160 bool GraphiteVulkanWindowContext::createSwapchain(int width, int height,
161                                                   const DisplayParams& params) {
162     // check for capabilities
163     VkSurfaceCapabilitiesKHR caps;
164     VkResult res = fGetPhysicalDeviceSurfaceCapabilitiesKHR(fPhysicalDevice, fSurface, &caps);
165     if (VK_SUCCESS != res) {
166         return false;
167     }
168 
169     uint32_t surfaceFormatCount;
170     res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
171                                               nullptr);
172     if (VK_SUCCESS != res) {
173         return false;
174     }
175 
176     SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR));
177     VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get();
178     res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
179                                               surfaceFormats);
180     if (VK_SUCCESS != res) {
181         return false;
182     }
183 
184     uint32_t presentModeCount;
185     res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
186                                                    nullptr);
187     if (VK_SUCCESS != res) {
188         return false;
189     }
190 
191     SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR));
192     VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get();
193     res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
194                                                    presentModes);
195     if (VK_SUCCESS != res) {
196         return false;
197     }
198 
199     VkExtent2D extent = caps.currentExtent;
200     // use the hints
201     if (extent.width == (uint32_t)-1) {
202         extent.width = width;
203         extent.height = height;
204     }
205 
206     // clamp width; to protect us from broken hints
207     if (extent.width < caps.minImageExtent.width) {
208         extent.width = caps.minImageExtent.width;
209     } else if (extent.width > caps.maxImageExtent.width) {
210         extent.width = caps.maxImageExtent.width;
211     }
212     // clamp height
213     if (extent.height < caps.minImageExtent.height) {
214         extent.height = caps.minImageExtent.height;
215     } else if (extent.height > caps.maxImageExtent.height) {
216         extent.height = caps.maxImageExtent.height;
217     }
218 
219     fWidth = (int)extent.width;
220     fHeight = (int)extent.height;
221 
222     uint32_t imageCount = caps.minImageCount + 2;
223     if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
224         // Application must settle for fewer images than desired:
225         imageCount = caps.maxImageCount;
226     }
227 
228     VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
229                                    VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
230                                    VK_IMAGE_USAGE_TRANSFER_DST_BIT;
231     SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags);
232     if (caps.supportedUsageFlags & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) {
233         usageFlags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
234     }
235     if (caps.supportedUsageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) {
236         usageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
237     }
238     SkASSERT(caps.supportedTransforms & caps.currentTransform);
239     SkASSERT(caps.supportedCompositeAlpha & (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
240                                              VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
241     VkCompositeAlphaFlagBitsKHR composite_alpha =
242         (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ?
243                                         VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR :
244                                         VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
245 
246     // Pick our surface format.
247     VkFormat surfaceFormat = VK_FORMAT_UNDEFINED;
248     VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
249     for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
250         VkFormat localFormat = surfaceFormats[i].format;
251         if (skgpu::graphite::vkFormatIsSupported(localFormat)) {
252             surfaceFormat = localFormat;
253             colorSpace = surfaceFormats[i].colorSpace;
254             break;
255         }
256     }
257     fDisplayParams = params;
258     fSampleCount = std::max(1, params.fMSAASampleCount);
259     fStencilBits = 8;
260 
261     if (VK_FORMAT_UNDEFINED == surfaceFormat) {
262         return false;
263     }
264 
265     SkColorType colorType;
266     switch (surfaceFormat) {
267         case VK_FORMAT_R8G8B8A8_UNORM: // fall through
268         case VK_FORMAT_R8G8B8A8_SRGB:
269             colorType = kRGBA_8888_SkColorType;
270             break;
271         case VK_FORMAT_B8G8R8A8_UNORM: // fall through
272             colorType = kBGRA_8888_SkColorType;
273             break;
274         default:
275             return false;
276     }
277 
278     // If mailbox mode is available, use it, as it is the lowest-latency non-
279     // tearing mode. If not, fall back to FIFO which is always available.
280     VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
281     bool hasImmediate = false;
282     for (uint32_t i = 0; i < presentModeCount; ++i) {
283         // use mailbox
284         if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) {
285             mode = VK_PRESENT_MODE_MAILBOX_KHR;
286         }
287         if (VK_PRESENT_MODE_IMMEDIATE_KHR == presentModes[i]) {
288             hasImmediate = true;
289         }
290     }
291     if (params.fDisableVsync && hasImmediate) {
292         mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
293     }
294 
295     VkSwapchainCreateInfoKHR swapchainCreateInfo;
296     memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
297     swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
298     swapchainCreateInfo.flags = fDisplayParams.fCreateProtectedNativeBackend
299                                 ? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR
300                                 : 0;
301     swapchainCreateInfo.surface = fSurface;
302     swapchainCreateInfo.minImageCount = imageCount;
303     swapchainCreateInfo.imageFormat = surfaceFormat;
304     swapchainCreateInfo.imageColorSpace = colorSpace;
305     swapchainCreateInfo.imageExtent = extent;
306     swapchainCreateInfo.imageArrayLayers = 1;
307     swapchainCreateInfo.imageUsage = usageFlags;
308 
309     uint32_t queueFamilies[] = { fGraphicsQueueIndex, fPresentQueueIndex };
310     if (fGraphicsQueueIndex != fPresentQueueIndex) {
311         swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
312         swapchainCreateInfo.queueFamilyIndexCount = 2;
313         swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
314     } else {
315         swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
316         swapchainCreateInfo.queueFamilyIndexCount = 0;
317         swapchainCreateInfo.pQueueFamilyIndices = nullptr;
318     }
319 
320     swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
321     swapchainCreateInfo.compositeAlpha = composite_alpha;
322     swapchainCreateInfo.presentMode = mode;
323     swapchainCreateInfo.clipped = true;
324     swapchainCreateInfo.oldSwapchain = fSwapchain;
325 
326     res = fCreateSwapchainKHR(fDevice, &swapchainCreateInfo, nullptr, &fSwapchain);
327     if (VK_SUCCESS != res) {
328         return false;
329     }
330 
331     // destroy the old swapchain
332     if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
333         fDeviceWaitIdle(fDevice);
334 
335         this->destroyBuffers();
336 
337         fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
338         swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE;
339     }
340 
341     if (!this->createBuffers(swapchainCreateInfo.imageFormat, usageFlags, colorType,
342                              swapchainCreateInfo.imageSharingMode)) {
343         fDeviceWaitIdle(fDevice);
344 
345         this->destroyBuffers();
346 
347         fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
348         swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE;
349     }
350 
351     return true;
352 }
353 
createBuffers(VkFormat format,VkImageUsageFlags usageFlags,SkColorType colorType,VkSharingMode sharingMode)354 bool GraphiteVulkanWindowContext::createBuffers(VkFormat format,
355                                                 VkImageUsageFlags usageFlags,
356                                                 SkColorType colorType,
357                                                 VkSharingMode sharingMode) {
358     fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, nullptr);
359     SkASSERT(fImageCount);
360     fImages = new VkImage[fImageCount];
361     fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, fImages);
362 
363     // set up initial image layouts and create surfaces
364     fImageLayouts = new VkImageLayout[fImageCount];
365     fSurfaces = new sk_sp<SkSurface>[fImageCount];
366     for (uint32_t i = 0; i < fImageCount; ++i) {
367         fImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
368 
369         skgpu::graphite::VulkanTextureInfo info;
370         info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
371         info.fFormat = format;
372         info.fImageUsageFlags = usageFlags;
373         info.fSharingMode = sharingMode;
374         info.fFlags = fDisplayParams.fCreateProtectedNativeBackend ? VK_IMAGE_CREATE_PROTECTED_BIT
375                                                                    : 0;
376 
377         skgpu::graphite::BackendTexture backendTex(this->dimensions(),
378                                                    info,
379                                                    VK_IMAGE_LAYOUT_UNDEFINED,
380                                                    fPresentQueueIndex,
381                                                    fImages[i],
382                                                    skgpu::VulkanAlloc());
383 
384         fSurfaces[i] = SkSurfaces::WrapBackendTexture(this->graphiteRecorder(),
385                                                       backendTex,
386                                                       colorType,
387                                                       fDisplayParams.fColorSpace,
388                                                       &fDisplayParams.fSurfaceProps);
389 
390         if (!fSurfaces[i]) {
391             return false;
392         }
393     }
394 
395     // set up the backbuffers
396     VkSemaphoreCreateInfo semaphoreInfo;
397     memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
398     semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
399     semaphoreInfo.pNext = nullptr;
400     semaphoreInfo.flags = 0;
401 
402     // we create one additional backbuffer structure here, because we want to
403     // give the command buffers they contain a chance to finish before we cycle back
404     fBackbuffers = new BackbufferInfo[fImageCount + 1];
405     for (uint32_t i = 0; i < fImageCount + 1; ++i) {
406         fBackbuffers[i].fImageIndex = -1;
407         VkResult result;
408         VULKAN_CALL_RESULT_NOCHECK(
409                 fInterface,
410                 result,
411                 CreateSemaphore(
412                         fDevice, &semaphoreInfo, nullptr, &fBackbuffers[i].fRenderSemaphore));
413     }
414     fCurrentBackbufferIndex = fImageCount;
415 
416     return true;
417 }
418 
destroyBuffers()419 void GraphiteVulkanWindowContext::destroyBuffers() {
420     if (fBackbuffers) {
421         for (uint32_t i = 0; i < fImageCount + 1; ++i) {
422             fBackbuffers[i].fImageIndex = -1;
423             VULKAN_CALL(fInterface,
424                         DestroySemaphore(fDevice,
425                                          fBackbuffers[i].fRenderSemaphore,
426                                          nullptr));
427         }
428     }
429 
430     delete[] fBackbuffers;
431     fBackbuffers = nullptr;
432 
433     // Does this actually free the surfaces?
434     delete[] fSurfaces;
435     fSurfaces = nullptr;
436     delete[] fImageLayouts;
437     fImageLayouts = nullptr;
438     delete[] fImages;
439     fImages = nullptr;
440 }
441 
~GraphiteVulkanWindowContext()442 GraphiteVulkanWindowContext::~GraphiteVulkanWindowContext() {
443    this->destroyContext();
444 }
445 
destroyContext()446 void GraphiteVulkanWindowContext::destroyContext() {
447     if (this->isValid()) {
448         fQueueWaitIdle(fPresentQueue);
449         fDeviceWaitIdle(fDevice);
450 
451         if (fWaitSemaphore != VK_NULL_HANDLE) {
452             VULKAN_CALL(fInterface, DestroySemaphore(fDevice, fWaitSemaphore, nullptr));
453             fWaitSemaphore = VK_NULL_HANDLE;
454         }
455 
456         this->destroyBuffers();
457 
458         if (fSwapchain != VK_NULL_HANDLE) {
459             fDestroySwapchainKHR(fDevice, fSwapchain, nullptr);
460             fSwapchain = VK_NULL_HANDLE;
461         }
462 
463         if (fSurface != VK_NULL_HANDLE) {
464             fDestroySurfaceKHR(fInstance, fSurface, nullptr);
465             fSurface = VK_NULL_HANDLE;
466         }
467     }
468 
469     if (fGraphiteContext) {
470         fGraphiteRecorder.reset();
471         fGraphiteContext.reset();
472     }
473     fInterface.reset();
474 
475     if (fDevice != VK_NULL_HANDLE) {
476         fDestroyDevice(fDevice, nullptr);
477         fDevice = VK_NULL_HANDLE;
478     }
479 
480 #ifdef SK_ENABLE_VK_LAYERS
481     if (fDebugCallback != VK_NULL_HANDLE) {
482         fDestroyDebugReportCallbackEXT(fInstance, fDebugCallback, nullptr);
483     }
484 #endif
485 
486     fPhysicalDevice = VK_NULL_HANDLE;
487 
488     if (fInstance != VK_NULL_HANDLE) {
489         fDestroyInstance(fInstance, nullptr);
490         fInstance = VK_NULL_HANDLE;
491     }
492 }
493 
getAvailableBackbuffer()494 GraphiteVulkanWindowContext::BackbufferInfo* GraphiteVulkanWindowContext::getAvailableBackbuffer() {
495     SkASSERT(fBackbuffers);
496 
497     ++fCurrentBackbufferIndex;
498     if (fCurrentBackbufferIndex > fImageCount) {
499         fCurrentBackbufferIndex = 0;
500     }
501 
502     BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
503     return backbuffer;
504 }
505 
getBackbufferSurface()506 sk_sp<SkSurface> GraphiteVulkanWindowContext::getBackbufferSurface() {
507     BackbufferInfo* backbuffer = this->getAvailableBackbuffer();
508     SkASSERT(backbuffer);
509 
510     // semaphores should be in unsignaled state
511     VkSemaphoreCreateInfo semaphoreInfo;
512     memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
513     semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
514     semaphoreInfo.pNext = nullptr;
515     semaphoreInfo.flags = 0;
516     VkResult result;
517     VULKAN_CALL_RESULT_NOCHECK(
518             fInterface, result, CreateSemaphore(fDevice, &semaphoreInfo, nullptr, &fWaitSemaphore));
519 
520     // acquire the image
521     VkResult res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
522                                         fWaitSemaphore, VK_NULL_HANDLE,
523                                         &backbuffer->fImageIndex);
524     if (VK_ERROR_SURFACE_LOST_KHR == res) {
525         // TODO: Recreate fSurface using fCreateVkSurfaceFn, and then rebuild the swapchain
526         VULKAN_CALL(fInterface, DestroySemaphore(fDevice, fWaitSemaphore, nullptr));
527         return nullptr;
528     }
529     if (VK_ERROR_OUT_OF_DATE_KHR == res) {
530         // tear swapchain down and try again
531         if (!this->createSwapchain(-1, -1, fDisplayParams)) {
532             VULKAN_CALL(fInterface, DestroySemaphore(fDevice, fWaitSemaphore, nullptr));
533             return nullptr;
534         }
535         backbuffer = this->getAvailableBackbuffer();
536 
537         // acquire the image
538         res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
539                                    fWaitSemaphore, VK_NULL_HANDLE,
540                                    &backbuffer->fImageIndex);
541 
542         if (VK_SUCCESS != res) {
543             VULKAN_CALL(fInterface, DestroySemaphore(fDevice, fWaitSemaphore, nullptr));
544             return nullptr;
545         }
546     }
547 
548     SkSurface* surface = fSurfaces[backbuffer->fImageIndex].get();
549 
550     return sk_ref_sp(surface);
551 }
552 
onSwapBuffers()553 void GraphiteVulkanWindowContext::onSwapBuffers() {
554     if (!fGraphiteContext) {
555         return;
556     }
557     SkASSERT(fGraphiteRecorder);
558 
559     BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
560 
561     // Rather than using snapRecordingAndSubmit we explicitly do that work here
562     // so we can set up the swapchain semaphores.
563     std::unique_ptr<skgpu::graphite::Recording> recording = fGraphiteRecorder->snap();
564     if (recording) {
565         skgpu::graphite::InsertRecordingInfo info;
566         info.fRecording = recording.get();
567 
568         // set up surface for layout transition
569         info.fTargetSurface = fSurfaces[backbuffer->fImageIndex].get();
570         skgpu::MutableTextureState presentState = skgpu::MutableTextureStates::MakeVulkan(
571                 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, fPresentQueueIndex);
572         info.fTargetTextureState = &presentState;
573 
574         SkASSERT(fWaitSemaphore != VK_NULL_HANDLE);
575         skgpu::graphite::BackendSemaphore beWaitSemaphore(fWaitSemaphore);
576         info.fNumWaitSemaphores = 1;
577         info.fWaitSemaphores = &beWaitSemaphore;
578         skgpu::graphite::BackendSemaphore beSignalSemaphore(backbuffer->fRenderSemaphore);
579         info.fNumSignalSemaphores = 1;
580         info.fSignalSemaphores = &beSignalSemaphore;
581 
582         // Insert finishedProc to delete waitSemaphore when done
583         struct FinishContext {
584             sk_sp<const skgpu::VulkanInterface> interface;
585             VkDevice device;
586             VkSemaphore waitSemaphore;
587         };
588         auto* finishContext = new FinishContext{fInterface, fDevice, fWaitSemaphore};
589         skgpu::graphite::GpuFinishedProc finishCallback = [](skgpu::graphite::GpuFinishedContext c,
590                                                                  skgpu::CallbackResult status) {
591             // regardless of the status we need to destroy the semaphore
592             const auto* context = reinterpret_cast<const FinishContext*>(c);
593             VULKAN_CALL(context->interface,
594                         DestroySemaphore(context->device, context->waitSemaphore, nullptr));
595         };
596         info.fFinishedContext = finishContext;
597         info.fFinishedProc = finishCallback;
598 
599         fGraphiteContext->insertRecording(info);
600         fGraphiteContext->submit(skgpu::graphite::SyncToCpu::kNo);
601         fWaitSemaphore = VK_NULL_HANDLE; // FinishCallback will destroy this
602     }
603 
604     // Submit present operation to present queue
605     const VkPresentInfoKHR presentInfo =
606     {
607         VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, // sType
608         nullptr, // pNext
609         1, // waitSemaphoreCount
610         &backbuffer->fRenderSemaphore, // pWaitSemaphores
611         1, // swapchainCount
612         &fSwapchain, // pSwapchains
613         &backbuffer->fImageIndex, // pImageIndices
614         nullptr // pResults
615     };
616 
617     fQueuePresentKHR(fPresentQueue, &presentInfo);
618 }
619 
620 }   //namespace skwindow::internal
621