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