1 /*
2 * Copyright 2023 Google LLC
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/SkVulkanWindowContext.h"
9
10 #include "include/core/SkSurface.h"
11 #include "include/gpu/GrBackendSemaphore.h"
12 #include "include/gpu/GrBackendSurface.h"
13 #include "include/gpu/GrDirectContext.h"
14 #include "src/base/SkAutoMalloc.h"
15
16 #include "include/gpu/vk/GrVkTypes.h"
17 #include "include/gpu/vk/VulkanExtensions.h"
18 #include "src/gpu/ganesh/vk/GrVkImage.h"
19 #include "src/gpu/ganesh/vk/GrVkUtil.h"
20 #include "src/gpu/vk/VulkanInterface.h"
21
22 #ifdef VK_USE_PLATFORM_WIN32_KHR
23 // windows wants to define this as CreateSemaphoreA or CreateSemaphoreW
24 #undef CreateSemaphore
25 #endif
26
27 #define GET_PROC(F) f ## F = \
28 (PFN_vk ## F) backendContext.fGetProc("vk" #F, fInstance, VK_NULL_HANDLE)
29 #define GET_DEV_PROC(F) f ## F = \
30 (PFN_vk ## F) backendContext.fGetProc("vk" #F, VK_NULL_HANDLE, fDevice)
31
SkVulkanWindowContext(const SkDisplayParams & params,CreateVkSurfaceFn createVkSurface,CanPresentFn canPresent,PFN_vkGetInstanceProcAddr instProc)32 SkVulkanWindowContext::SkVulkanWindowContext(const SkDisplayParams& params,
33 CreateVkSurfaceFn createVkSurface,
34 CanPresentFn canPresent,
35 PFN_vkGetInstanceProcAddr instProc)
36 : SkWindowContext(params)
37 , fCreateVkSurfaceFn(createVkSurface)
38 , fCanPresentFn(canPresent)
39 , fSurface(VK_NULL_HANDLE)
40 , fSwapchain(VK_NULL_HANDLE)
41 , fImages(nullptr)
42 , fImageLayouts(nullptr)
43 , fSurfaces(nullptr)
44 , fBackbuffers(nullptr) {
45 fGetInstanceProcAddr = instProc;
46 this->initializeContext();
47 }
48
initializeContext()49 void SkVulkanWindowContext::initializeContext() {
50 SkASSERT(!fContext);
51 // any config code here (particularly for msaa)?
52
53 PFN_vkGetInstanceProcAddr getInstanceProc = fGetInstanceProcAddr;
54 GrVkBackendContext backendContext;
55 skgpu::VulkanExtensions extensions;
56 VkPhysicalDeviceFeatures2 features;
57 if (!sk_gpu_test::CreateVkBackendContext(getInstanceProc, &backendContext, &extensions,
58 &features, &fDebugCallback, &fPresentQueueIndex,
59 fCanPresentFn)) {
60 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
61 return;
62 }
63
64 if (!extensions.hasExtension(VK_KHR_SURFACE_EXTENSION_NAME, 25) ||
65 !extensions.hasExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, 68)) {
66 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
67 return;
68 }
69
70 fInstance = backendContext.fInstance;
71 fPhysicalDevice = backendContext.fPhysicalDevice;
72 fDevice = backendContext.fDevice;
73 fGraphicsQueueIndex = backendContext.fGraphicsQueueIndex;
74 fGraphicsQueue = backendContext.fQueue;
75
76 PFN_vkGetPhysicalDeviceProperties localGetPhysicalDeviceProperties =
77 reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(
78 backendContext.fGetProc("vkGetPhysicalDeviceProperties",
79 backendContext.fInstance,
80 VK_NULL_HANDLE));
81 if (!localGetPhysicalDeviceProperties) {
82 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
83 return;
84 }
85 VkPhysicalDeviceProperties physDeviceProperties;
86 localGetPhysicalDeviceProperties(backendContext.fPhysicalDevice, &physDeviceProperties);
87 uint32_t physDevVersion = physDeviceProperties.apiVersion;
88
89 fInterface.reset(new skgpu::VulkanInterface(backendContext.fGetProc, fInstance, fDevice,
90 backendContext.fInstanceVersion, physDevVersion,
91 &extensions));
92
93 GET_PROC(DestroyInstance);
94 if (fDebugCallback != VK_NULL_HANDLE) {
95 GET_PROC(DestroyDebugReportCallbackEXT);
96 }
97 GET_PROC(DestroySurfaceKHR);
98 GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
99 GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
100 GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
101 GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
102 GET_DEV_PROC(DeviceWaitIdle);
103 GET_DEV_PROC(QueueWaitIdle);
104 GET_DEV_PROC(DestroyDevice);
105 GET_DEV_PROC(CreateSwapchainKHR);
106 GET_DEV_PROC(DestroySwapchainKHR);
107 GET_DEV_PROC(GetSwapchainImagesKHR);
108 GET_DEV_PROC(AcquireNextImageKHR);
109 GET_DEV_PROC(QueuePresentKHR);
110 GET_DEV_PROC(GetDeviceQueue);
111
112 fContext = GrDirectContext::MakeVulkan(backendContext, fDisplayParams.fGrContextOptions);
113
114 fSurface = fCreateVkSurfaceFn(fInstance);
115 if (VK_NULL_HANDLE == fSurface) {
116 this->destroyContext();
117 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
118 return;
119 }
120
121 VkBool32 supported;
122 VkResult res = fGetPhysicalDeviceSurfaceSupportKHR(fPhysicalDevice, fPresentQueueIndex,
123 fSurface, &supported);
124 if (VK_SUCCESS != res) {
125 this->destroyContext();
126 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
127 return;
128 }
129
130 if (!this->createSwapchain(-1, -1, fDisplayParams)) {
131 this->destroyContext();
132 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
133 return;
134 }
135
136 // create presentQueue
137 fGetDeviceQueue(fDevice, fPresentQueueIndex, 0, &fPresentQueue);
138 sk_gpu_test::FreeVulkanFeaturesStructs(&features);
139 }
140
createSwapchain(int width,int height,const SkDisplayParams & params)141 bool SkVulkanWindowContext::createSwapchain(int width, int height,
142 const SkDisplayParams& params) {
143 // check for capabilities
144 VkSurfaceCapabilitiesKHR caps;
145 VkResult res = fGetPhysicalDeviceSurfaceCapabilitiesKHR(fPhysicalDevice, fSurface, &caps);
146 if (VK_SUCCESS != res) {
147 return false;
148 }
149
150 uint32_t surfaceFormatCount;
151 res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
152 nullptr);
153 if (VK_SUCCESS != res) {
154 return false;
155 }
156
157 SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR));
158 VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get();
159 res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
160 surfaceFormats);
161 if (VK_SUCCESS != res) {
162 return false;
163 }
164
165 uint32_t presentModeCount;
166 res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
167 nullptr);
168 if (VK_SUCCESS != res) {
169 return false;
170 }
171
172 SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR));
173 VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get();
174 res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
175 presentModes);
176 if (VK_SUCCESS != res) {
177 return false;
178 }
179
180 VkExtent2D extent = caps.currentExtent;
181 // use the hints
182 if (extent.width == (uint32_t)-1) {
183 extent.width = width;
184 extent.height = height;
185 }
186
187 // clamp width; to protect us from broken hints
188 if (extent.width < caps.minImageExtent.width) {
189 extent.width = caps.minImageExtent.width;
190 } else if (extent.width > caps.maxImageExtent.width) {
191 extent.width = caps.maxImageExtent.width;
192 }
193 // clamp height
194 if (extent.height < caps.minImageExtent.height) {
195 extent.height = caps.minImageExtent.height;
196 } else if (extent.height > caps.maxImageExtent.height) {
197 extent.height = caps.maxImageExtent.height;
198 }
199
200 fWidth = (int)extent.width;
201 fHeight = (int)extent.height;
202
203 uint32_t imageCount = caps.minImageCount + 2;
204 if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
205 // Application must settle for fewer images than desired:
206 imageCount = caps.maxImageCount;
207 }
208
209 VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
210 VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
211 VK_IMAGE_USAGE_TRANSFER_DST_BIT;
212 SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags);
213 if (caps.supportedUsageFlags & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) {
214 usageFlags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
215 }
216 if (caps.supportedUsageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) {
217 usageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
218 }
219 SkASSERT(caps.supportedTransforms & caps.currentTransform);
220 SkASSERT(caps.supportedCompositeAlpha & (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
221 VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
222 VkCompositeAlphaFlagBitsKHR composite_alpha =
223 (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ?
224 VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR :
225 VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
226
227 // Pick our surface format.
228 VkFormat surfaceFormat = VK_FORMAT_UNDEFINED;
229 VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
230 for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
231 VkFormat localFormat = surfaceFormats[i].format;
232 if (GrVkFormatIsSupported(localFormat)) {
233 surfaceFormat = localFormat;
234 colorSpace = surfaceFormats[i].colorSpace;
235 break;
236 }
237 }
238 fDisplayParams = params;
239 fSampleCount = std::max(1, params.fMSAASampleCount);
240 fStencilBits = 8;
241
242 if (VK_FORMAT_UNDEFINED == surfaceFormat) {
243 return false;
244 }
245
246 SkColorType colorType;
247 switch (surfaceFormat) {
248 case VK_FORMAT_R8G8B8A8_UNORM: // fall through
249 case VK_FORMAT_R8G8B8A8_SRGB:
250 colorType = kRGBA_8888_SkColorType;
251 break;
252 case VK_FORMAT_B8G8R8A8_UNORM: // fall through
253 colorType = kBGRA_8888_SkColorType;
254 break;
255 default:
256 return false;
257 }
258
259 // If mailbox mode is available, use it, as it is the lowest-latency non-
260 // tearing mode. If not, fall back to FIFO which is always available.
261 VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
262 bool hasImmediate = false;
263 for (uint32_t i = 0; i < presentModeCount; ++i) {
264 // use mailbox
265 if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) {
266 mode = VK_PRESENT_MODE_MAILBOX_KHR;
267 }
268 if (VK_PRESENT_MODE_IMMEDIATE_KHR == presentModes[i]) {
269 hasImmediate = true;
270 }
271 }
272 if (params.fDisableVsync && hasImmediate) {
273 mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
274 }
275
276 VkSwapchainCreateInfoKHR swapchainCreateInfo;
277 memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
278 swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
279 swapchainCreateInfo.surface = fSurface;
280 swapchainCreateInfo.minImageCount = imageCount;
281 swapchainCreateInfo.imageFormat = surfaceFormat;
282 swapchainCreateInfo.imageColorSpace = colorSpace;
283 swapchainCreateInfo.imageExtent = extent;
284 swapchainCreateInfo.imageArrayLayers = 1;
285 swapchainCreateInfo.imageUsage = usageFlags;
286
287 uint32_t queueFamilies[] = { fGraphicsQueueIndex, fPresentQueueIndex };
288 if (fGraphicsQueueIndex != fPresentQueueIndex) {
289 swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
290 swapchainCreateInfo.queueFamilyIndexCount = 2;
291 swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
292 } else {
293 swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
294 swapchainCreateInfo.queueFamilyIndexCount = 0;
295 swapchainCreateInfo.pQueueFamilyIndices = nullptr;
296 }
297
298 swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
299 swapchainCreateInfo.compositeAlpha = composite_alpha;
300 swapchainCreateInfo.presentMode = mode;
301 swapchainCreateInfo.clipped = true;
302 swapchainCreateInfo.oldSwapchain = fSwapchain;
303
304 res = fCreateSwapchainKHR(fDevice, &swapchainCreateInfo, nullptr, &fSwapchain);
305 if (VK_SUCCESS != res) {
306 return false;
307 }
308
309 // destroy the old swapchain
310 if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
311 fDeviceWaitIdle(fDevice);
312
313 this->destroyBuffers();
314
315 fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
316 }
317
318 if (!this->createBuffers(swapchainCreateInfo.imageFormat, usageFlags, colorType,
319 swapchainCreateInfo.imageSharingMode)) {
320 fDeviceWaitIdle(fDevice);
321
322 this->destroyBuffers();
323
324 fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
325 }
326
327 return true;
328 }
329
createBuffers(VkFormat format,VkImageUsageFlags usageFlags,SkColorType colorType,VkSharingMode sharingMode)330 bool SkVulkanWindowContext::createBuffers(VkFormat format, VkImageUsageFlags usageFlags,
331 SkColorType colorType,
332 VkSharingMode sharingMode) {
333 fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, nullptr);
334 SkASSERT(fImageCount);
335 fImages = new VkImage[fImageCount];
336 fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, fImages);
337
338 // set up initial image layouts and create surfaces
339 fImageLayouts = new VkImageLayout[fImageCount];
340 fSurfaces = new sk_sp<SkSurface>[fImageCount];
341 for (uint32_t i = 0; i < fImageCount; ++i) {
342 fImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
343
344 GrVkImageInfo info;
345 info.fImage = fImages[i];
346 info.fAlloc = skgpu::VulkanAlloc();
347 info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
348 info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
349 info.fFormat = format;
350 info.fImageUsageFlags = usageFlags;
351 info.fLevelCount = 1;
352 info.fCurrentQueueFamily = fPresentQueueIndex;
353 info.fSharingMode = sharingMode;
354
355 if (usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) {
356 GrBackendTexture backendTexture(fWidth, fHeight, info);
357 fSurfaces[i] = SkSurface::MakeFromBackendTexture(
358 fContext.get(), backendTexture, kTopLeft_GrSurfaceOrigin,
359 fDisplayParams.fMSAASampleCount,
360 colorType, fDisplayParams.fColorSpace, &fDisplayParams.fSurfaceProps);
361 } else {
362 if (fDisplayParams.fMSAASampleCount > 1) {
363 return false;
364 }
365 GrBackendRenderTarget backendRT(fWidth, fHeight, fSampleCount, info);
366 fSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(
367 fContext.get(), backendRT, kTopLeft_GrSurfaceOrigin, colorType,
368 fDisplayParams.fColorSpace, &fDisplayParams.fSurfaceProps);
369
370 }
371 if (!fSurfaces[i]) {
372 return false;
373 }
374 }
375
376 // set up the backbuffers
377 VkSemaphoreCreateInfo semaphoreInfo;
378 memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
379 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
380 semaphoreInfo.pNext = nullptr;
381 semaphoreInfo.flags = 0;
382
383 // we create one additional backbuffer structure here, because we want to
384 // give the command buffers they contain a chance to finish before we cycle back
385 fBackbuffers = new BackbufferInfo[fImageCount + 1];
386 for (uint32_t i = 0; i < fImageCount + 1; ++i) {
387 fBackbuffers[i].fImageIndex = -1;
388 SkDEBUGCODE(VkResult result = )GR_VK_CALL(fInterface,
389 CreateSemaphore(fDevice, &semaphoreInfo, nullptr,
390 &fBackbuffers[i].fRenderSemaphore));
391 SkASSERT(result == VK_SUCCESS);
392 }
393 fCurrentBackbufferIndex = fImageCount;
394 return true;
395 }
396
destroyBuffers()397 void SkVulkanWindowContext::destroyBuffers() {
398
399 if (fBackbuffers) {
400 for (uint32_t i = 0; i < fImageCount + 1; ++i) {
401 fBackbuffers[i].fImageIndex = -1;
402 GR_VK_CALL(fInterface,
403 DestroySemaphore(fDevice,
404 fBackbuffers[i].fRenderSemaphore,
405 nullptr));
406 }
407 }
408
409 delete[] fBackbuffers;
410 fBackbuffers = nullptr;
411
412 // Does this actually free the surfaces?
413 delete[] fSurfaces;
414 fSurfaces = nullptr;
415 delete[] fImageLayouts;
416 fImageLayouts = nullptr;
417 delete[] fImages;
418 fImages = nullptr;
419 }
420
~SkVulkanWindowContext()421 SkVulkanWindowContext::~SkVulkanWindowContext() {
422 this->destroyContext();
423 }
424
destroyContext()425 void SkVulkanWindowContext::destroyContext() {
426 if (this->isValid()) {
427 fQueueWaitIdle(fPresentQueue);
428 fDeviceWaitIdle(fDevice);
429
430 this->destroyBuffers();
431
432 if (VK_NULL_HANDLE != fSwapchain) {
433 fDestroySwapchainKHR(fDevice, fSwapchain, nullptr);
434 fSwapchain = VK_NULL_HANDLE;
435 }
436
437 if (VK_NULL_HANDLE != fSurface) {
438 fDestroySurfaceKHR(fInstance, fSurface, nullptr);
439 fSurface = VK_NULL_HANDLE;
440 }
441 }
442
443 SkASSERT(fContext->unique());
444 fContext.reset();
445 fInterface.reset();
446
447 if (VK_NULL_HANDLE != fDevice) {
448 fDestroyDevice(fDevice, nullptr);
449 fDevice = VK_NULL_HANDLE;
450 }
451
452 #ifdef SK_ENABLE_VK_LAYERS
453 if (fDebugCallback != VK_NULL_HANDLE) {
454 fDestroyDebugReportCallbackEXT(fInstance, fDebugCallback, nullptr);
455 }
456 #endif
457
458 fPhysicalDevice = VK_NULL_HANDLE;
459
460 if (VK_NULL_HANDLE != fInstance) {
461 fDestroyInstance(fInstance, nullptr);
462 fInstance = VK_NULL_HANDLE;
463 }
464 }
465
getAvailableBackbuffer()466 SkVulkanWindowContext::BackbufferInfo* SkVulkanWindowContext::getAvailableBackbuffer() {
467 SkASSERT(fBackbuffers);
468
469 ++fCurrentBackbufferIndex;
470 if (fCurrentBackbufferIndex > fImageCount) {
471 fCurrentBackbufferIndex = 0;
472 }
473
474 BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
475 return backbuffer;
476 }
477
getBackbufferSurface()478 sk_sp<SkSurface> SkVulkanWindowContext::getBackbufferSurface() {
479 BackbufferInfo* backbuffer = this->getAvailableBackbuffer();
480 SkASSERT(backbuffer);
481
482 // semaphores should be in unsignaled state
483 VkSemaphoreCreateInfo semaphoreInfo;
484 memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
485 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
486 semaphoreInfo.pNext = nullptr;
487 semaphoreInfo.flags = 0;
488 VkSemaphore semaphore;
489 SkDEBUGCODE(VkResult result = )GR_VK_CALL(fInterface, CreateSemaphore(fDevice, &semaphoreInfo,
490 nullptr, &semaphore));
491 SkASSERT(result == VK_SUCCESS);
492
493 // acquire the image
494 VkResult res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
495 semaphore, VK_NULL_HANDLE,
496 &backbuffer->fImageIndex);
497 if (VK_ERROR_SURFACE_LOST_KHR == res) {
498 // need to figure out how to create a new vkSurface without the platformData*
499 // maybe use attach somehow? but need a Window
500 GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
501 return nullptr;
502 }
503 if (VK_ERROR_OUT_OF_DATE_KHR == res) {
504 // tear swapchain down and try again
505 if (!this->createSwapchain(-1, -1, fDisplayParams)) {
506 GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
507 return nullptr;
508 }
509 backbuffer = this->getAvailableBackbuffer();
510
511 // acquire the image
512 res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
513 semaphore, VK_NULL_HANDLE,
514 &backbuffer->fImageIndex);
515
516 if (VK_SUCCESS != res) {
517 GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
518 return nullptr;
519 }
520 }
521
522 SkSurface* surface = fSurfaces[backbuffer->fImageIndex].get();
523
524 GrBackendSemaphore beSemaphore;
525 beSemaphore.initVulkan(semaphore);
526
527 surface->wait(1, &beSemaphore);
528
529 return sk_ref_sp(surface);
530 }
531
swapBuffers()532 void SkVulkanWindowContext::swapBuffers() {
533
534 BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
535 SkSurface* surface = fSurfaces[backbuffer->fImageIndex].get();
536
537 GrBackendSemaphore beSemaphore;
538 beSemaphore.initVulkan(backbuffer->fRenderSemaphore);
539
540 GrFlushInfo info;
541 info.fNumSemaphores = 1;
542 info.fSignalSemaphores = &beSemaphore;
543 skgpu::MutableTextureState presentState(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, fPresentQueueIndex);
544 surface->flush(info, &presentState);
545 surface->recordingContext()->asDirectContext()->submit();
546
547 // Submit present operation to present queue
548 const VkPresentInfoKHR presentInfo =
549 {
550 VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, // sType
551 nullptr, // pNext
552 1, // waitSemaphoreCount
553 &backbuffer->fRenderSemaphore, // pWaitSemaphores
554 1, // swapchainCount
555 &fSwapchain, // pSwapchains
556 &backbuffer->fImageIndex, // pImageIndices
557 nullptr // pResults
558 };
559
560 fQueuePresentKHR(fPresentQueue, &presentInfo);
561 }
562