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