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