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