2 * Copyright © 2017 Intel Corporation
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 #include "wsi_common_private.h"
25 #include "util/macros.h"
29 wsi_device_init(struct wsi_device
*wsi
,
30 VkPhysicalDevice pdevice
,
31 WSI_FN_GetPhysicalDeviceProcAddr proc_addr
)
33 memset(wsi
, 0, sizeof(*wsi
));
35 #define WSI_GET_CB(func) \
36 PFN_vk##func func = (PFN_vk##func)proc_addr(pdevice, "vk" #func)
37 WSI_GET_CB(GetPhysicalDeviceMemoryProperties
);
38 WSI_GET_CB(GetPhysicalDeviceQueueFamilyProperties
);
41 GetPhysicalDeviceMemoryProperties(pdevice
, &wsi
->memory_props
);
42 GetPhysicalDeviceQueueFamilyProperties(pdevice
, &wsi
->queue_family_count
, NULL
);
44 #define WSI_GET_CB(func) \
45 wsi->func = (PFN_vk##func)proc_addr(pdevice, "vk" #func)
46 WSI_GET_CB(AllocateMemory
);
47 WSI_GET_CB(AllocateCommandBuffers
);
48 WSI_GET_CB(BindBufferMemory
);
49 WSI_GET_CB(BindImageMemory
);
50 WSI_GET_CB(BeginCommandBuffer
);
51 WSI_GET_CB(CmdCopyImageToBuffer
);
52 WSI_GET_CB(CreateBuffer
);
53 WSI_GET_CB(CreateCommandPool
);
54 WSI_GET_CB(CreateFence
);
55 WSI_GET_CB(CreateImage
);
56 WSI_GET_CB(DestroyBuffer
);
57 WSI_GET_CB(DestroyCommandPool
);
58 WSI_GET_CB(DestroyFence
);
59 WSI_GET_CB(DestroyImage
);
60 WSI_GET_CB(EndCommandBuffer
);
61 WSI_GET_CB(FreeMemory
);
62 WSI_GET_CB(FreeCommandBuffers
);
63 WSI_GET_CB(GetBufferMemoryRequirements
);
64 WSI_GET_CB(GetImageMemoryRequirements
);
65 WSI_GET_CB(GetImageSubresourceLayout
);
66 WSI_GET_CB(GetMemoryFdKHR
);
67 WSI_GET_CB(ResetFences
);
68 WSI_GET_CB(QueueSubmit
);
69 WSI_GET_CB(WaitForFences
);
74 wsi_swapchain_init(const struct wsi_device
*wsi
,
75 struct wsi_swapchain
*chain
,
77 const VkSwapchainCreateInfoKHR
*pCreateInfo
,
78 const VkAllocationCallbacks
*pAllocator
)
82 memset(chain
, 0, sizeof(*chain
));
85 chain
->device
= device
;
86 chain
->alloc
= *pAllocator
;
89 vk_zalloc(pAllocator
, sizeof(VkCommandPool
) * wsi
->queue_family_count
, 8,
90 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT
);
91 if (!chain
->cmd_pools
)
92 return VK_ERROR_OUT_OF_HOST_MEMORY
;
94 for (uint32_t i
= 0; i
< wsi
->queue_family_count
; i
++) {
95 const VkCommandPoolCreateInfo cmd_pool_info
= {
96 .sType
= VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO
,
99 .queueFamilyIndex
= i
,
101 result
= wsi
->CreateCommandPool(device
, &cmd_pool_info
, &chain
->alloc
,
102 &chain
->cmd_pools
[i
]);
103 if (result
!= VK_SUCCESS
)
110 wsi_swapchain_finish(chain
);
115 wsi_swapchain_finish(struct wsi_swapchain
*chain
)
117 for (uint32_t i
= 0; i
< chain
->wsi
->queue_family_count
; i
++) {
118 chain
->wsi
->DestroyCommandPool(chain
->device
, chain
->cmd_pools
[i
],
124 select_memory_type(const struct wsi_device
*wsi
,
125 VkMemoryPropertyFlags props
,
128 for (uint32_t i
= 0; i
< wsi
->memory_props
.memoryTypeCount
; i
++) {
129 const VkMemoryType type
= wsi
->memory_props
.memoryTypes
[i
];
130 if ((type_bits
& (1 << i
)) && (type
.propertyFlags
& props
) == props
)
134 unreachable("No memory type found");
138 vk_format_size(VkFormat format
)
141 case VK_FORMAT_B8G8R8A8_UNORM
:
142 case VK_FORMAT_B8G8R8A8_SRGB
:
145 unreachable("Unknown WSI Format");
149 static inline uint32_t
150 align_u32(uint32_t v
, uint32_t a
)
152 assert(a
!= 0 && a
== (a
& -a
));
153 return (v
+ a
- 1) & ~(a
- 1);
157 wsi_create_native_image(const struct wsi_swapchain
*chain
,
158 const VkSwapchainCreateInfoKHR
*pCreateInfo
,
159 struct wsi_image
*image
)
161 const struct wsi_device
*wsi
= chain
->wsi
;
164 memset(image
, 0, sizeof(*image
));
166 const struct wsi_image_create_info image_wsi_info
= {
167 .sType
= VK_STRUCTURE_TYPE_WSI_IMAGE_CREATE_INFO_MESA
,
171 const VkImageCreateInfo image_info
= {
172 .sType
= VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO
,
173 .pNext
= &image_wsi_info
,
175 .imageType
= VK_IMAGE_TYPE_2D
,
176 .format
= pCreateInfo
->imageFormat
,
178 .width
= pCreateInfo
->imageExtent
.width
,
179 .height
= pCreateInfo
->imageExtent
.height
,
184 .samples
= VK_SAMPLE_COUNT_1_BIT
,
185 .tiling
= VK_IMAGE_TILING_OPTIMAL
,
186 .usage
= pCreateInfo
->imageUsage
,
187 .sharingMode
= pCreateInfo
->imageSharingMode
,
188 .queueFamilyIndexCount
= pCreateInfo
->queueFamilyIndexCount
,
189 .pQueueFamilyIndices
= pCreateInfo
->pQueueFamilyIndices
,
190 .initialLayout
= VK_IMAGE_LAYOUT_UNDEFINED
,
192 result
= wsi
->CreateImage(chain
->device
, &image_info
,
193 &chain
->alloc
, &image
->image
);
194 if (result
!= VK_SUCCESS
)
197 VkMemoryRequirements reqs
;
198 wsi
->GetImageMemoryRequirements(chain
->device
, image
->image
, &reqs
);
200 VkSubresourceLayout image_layout
;
201 const VkImageSubresource image_subresource
= {
202 .aspectMask
= VK_IMAGE_ASPECT_COLOR_BIT
,
206 wsi
->GetImageSubresourceLayout(chain
->device
, image
->image
,
207 &image_subresource
, &image_layout
);
209 const struct wsi_memory_allocate_info memory_wsi_info
= {
210 .sType
= VK_STRUCTURE_TYPE_WSI_MEMORY_ALLOCATE_INFO_MESA
,
212 .implicit_sync
= true,
214 const VkExportMemoryAllocateInfoKHR memory_export_info
= {
215 .sType
= VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR
,
216 .pNext
= &memory_wsi_info
,
217 .handleTypes
= VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT
,
219 const VkMemoryDedicatedAllocateInfoKHR memory_dedicated_info
= {
220 .sType
= VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR
,
221 .pNext
= &memory_export_info
,
222 .image
= image
->image
,
223 .buffer
= VK_NULL_HANDLE
,
225 const VkMemoryAllocateInfo memory_info
= {
226 .sType
= VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
,
227 .pNext
= &memory_dedicated_info
,
228 .allocationSize
= reqs
.size
,
229 .memoryTypeIndex
= select_memory_type(wsi
, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
,
230 reqs
.memoryTypeBits
),
232 result
= wsi
->AllocateMemory(chain
->device
, &memory_info
,
233 &chain
->alloc
, &image
->memory
);
234 if (result
!= VK_SUCCESS
)
237 result
= wsi
->BindImageMemory(chain
->device
, image
->image
,
239 if (result
!= VK_SUCCESS
)
242 const VkMemoryGetFdInfoKHR memory_get_fd_info
= {
243 .sType
= VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR
,
245 .memory
= image
->memory
,
246 .handleType
= VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT
,
249 result
= wsi
->GetMemoryFdKHR(chain
->device
, &memory_get_fd_info
, &fd
);
250 if (result
!= VK_SUCCESS
)
253 image
->size
= reqs
.size
;
254 image
->row_pitch
= image_layout
.rowPitch
;
261 wsi_destroy_image(chain
, image
);
266 #define WSI_PRIME_LINEAR_STRIDE_ALIGN 256
269 wsi_create_prime_image(const struct wsi_swapchain
*chain
,
270 const VkSwapchainCreateInfoKHR
*pCreateInfo
,
271 struct wsi_image
*image
)
273 const struct wsi_device
*wsi
= chain
->wsi
;
276 memset(image
, 0, sizeof(*image
));
278 const uint32_t cpp
= vk_format_size(pCreateInfo
->imageFormat
);
279 const uint32_t linear_stride
= align_u32(pCreateInfo
->imageExtent
.width
* cpp
,
280 WSI_PRIME_LINEAR_STRIDE_ALIGN
);
282 uint32_t linear_size
= linear_stride
* pCreateInfo
->imageExtent
.height
;
283 linear_size
= align_u32(linear_size
, 4096);
285 const VkExternalMemoryBufferCreateInfoKHR prime_buffer_external_info
= {
286 .sType
= VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR
,
288 .handleTypes
= VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT
,
290 const VkBufferCreateInfo prime_buffer_info
= {
291 .sType
= VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO
,
292 .pNext
= &prime_buffer_external_info
,
294 .usage
= VK_BUFFER_USAGE_TRANSFER_DST_BIT
,
295 .sharingMode
= VK_SHARING_MODE_EXCLUSIVE
,
297 result
= wsi
->CreateBuffer(chain
->device
, &prime_buffer_info
,
298 &chain
->alloc
, &image
->prime
.buffer
);
299 if (result
!= VK_SUCCESS
)
302 VkMemoryRequirements reqs
;
303 wsi
->GetBufferMemoryRequirements(chain
->device
, image
->prime
.buffer
, &reqs
);
304 assert(reqs
.size
<= linear_size
);
306 const struct wsi_memory_allocate_info memory_wsi_info
= {
307 .sType
= VK_STRUCTURE_TYPE_WSI_MEMORY_ALLOCATE_INFO_MESA
,
309 .implicit_sync
= true,
311 const VkExportMemoryAllocateInfoKHR prime_memory_export_info
= {
312 .sType
= VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR
,
313 .pNext
= &memory_wsi_info
,
314 .handleTypes
= VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT
,
316 const VkMemoryDedicatedAllocateInfoKHR prime_memory_dedicated_info
= {
317 .sType
= VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR
,
318 .pNext
= &prime_memory_export_info
,
319 .image
= VK_NULL_HANDLE
,
320 .buffer
= image
->prime
.buffer
,
322 const VkMemoryAllocateInfo prime_memory_info
= {
323 .sType
= VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
,
324 .pNext
= &prime_memory_dedicated_info
,
325 .allocationSize
= linear_size
,
326 .memoryTypeIndex
= select_memory_type(wsi
, 0, reqs
.memoryTypeBits
),
328 result
= wsi
->AllocateMemory(chain
->device
, &prime_memory_info
,
329 &chain
->alloc
, &image
->prime
.memory
);
330 if (result
!= VK_SUCCESS
)
333 result
= wsi
->BindBufferMemory(chain
->device
, image
->prime
.buffer
,
334 image
->prime
.memory
, 0);
335 if (result
!= VK_SUCCESS
)
338 const VkImageCreateInfo image_info
= {
339 .sType
= VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO
,
342 .imageType
= VK_IMAGE_TYPE_2D
,
343 .format
= pCreateInfo
->imageFormat
,
345 .width
= pCreateInfo
->imageExtent
.width
,
346 .height
= pCreateInfo
->imageExtent
.height
,
351 .samples
= VK_SAMPLE_COUNT_1_BIT
,
352 .tiling
= VK_IMAGE_TILING_OPTIMAL
,
353 .usage
= pCreateInfo
->imageUsage
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT
,
354 .sharingMode
= pCreateInfo
->imageSharingMode
,
355 .queueFamilyIndexCount
= pCreateInfo
->queueFamilyIndexCount
,
356 .pQueueFamilyIndices
= pCreateInfo
->pQueueFamilyIndices
,
357 .initialLayout
= VK_IMAGE_LAYOUT_UNDEFINED
,
359 result
= wsi
->CreateImage(chain
->device
, &image_info
,
360 &chain
->alloc
, &image
->image
);
361 if (result
!= VK_SUCCESS
)
364 wsi
->GetImageMemoryRequirements(chain
->device
, image
->image
, &reqs
);
366 const VkMemoryDedicatedAllocateInfoKHR memory_dedicated_info
= {
367 .sType
= VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR
,
369 .image
= image
->image
,
370 .buffer
= VK_NULL_HANDLE
,
372 const VkMemoryAllocateInfo memory_info
= {
373 .sType
= VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
,
374 .pNext
= &memory_dedicated_info
,
375 .allocationSize
= reqs
.size
,
376 .memoryTypeIndex
= select_memory_type(wsi
, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
,
377 reqs
.memoryTypeBits
),
379 result
= wsi
->AllocateMemory(chain
->device
, &memory_info
,
380 &chain
->alloc
, &image
->memory
);
381 if (result
!= VK_SUCCESS
)
384 result
= wsi
->BindImageMemory(chain
->device
, image
->image
,
386 if (result
!= VK_SUCCESS
)
389 image
->prime
.blit_cmd_buffers
=
390 vk_zalloc(&chain
->alloc
,
391 sizeof(VkCommandBuffer
) * wsi
->queue_family_count
, 8,
392 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT
);
393 if (!image
->prime
.blit_cmd_buffers
)
396 for (uint32_t i
= 0; i
< wsi
->queue_family_count
; i
++) {
397 const VkCommandBufferAllocateInfo cmd_buffer_info
= {
398 .sType
= VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO
,
400 .commandPool
= chain
->cmd_pools
[i
],
401 .level
= VK_COMMAND_BUFFER_LEVEL_PRIMARY
,
402 .commandBufferCount
= 1,
404 result
= wsi
->AllocateCommandBuffers(chain
->device
, &cmd_buffer_info
,
405 &image
->prime
.blit_cmd_buffers
[i
]);
406 if (result
!= VK_SUCCESS
)
409 const VkCommandBufferBeginInfo begin_info
= {
410 .sType
= VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
,
412 wsi
->BeginCommandBuffer(image
->prime
.blit_cmd_buffers
[i
], &begin_info
);
414 struct VkBufferImageCopy buffer_image_copy
= {
416 .bufferRowLength
= linear_stride
/ cpp
,
417 .bufferImageHeight
= 0,
418 .imageSubresource
= {
419 .aspectMask
= VK_IMAGE_ASPECT_COLOR_BIT
,
424 .imageOffset
= { .x
= 0, .y
= 0, .z
= 0 },
426 .width
= pCreateInfo
->imageExtent
.width
,
427 .height
= pCreateInfo
->imageExtent
.height
,
431 wsi
->CmdCopyImageToBuffer(image
->prime
.blit_cmd_buffers
[i
],
433 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
,
435 1, &buffer_image_copy
);
437 result
= wsi
->EndCommandBuffer(image
->prime
.blit_cmd_buffers
[i
]);
438 if (result
!= VK_SUCCESS
)
442 const VkMemoryGetFdInfoKHR linear_memory_get_fd_info
= {
443 .sType
= VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR
,
445 .memory
= image
->prime
.memory
,
446 .handleType
= VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT
,
449 result
= wsi
->GetMemoryFdKHR(chain
->device
, &linear_memory_get_fd_info
, &fd
);
450 if (result
!= VK_SUCCESS
)
453 image
->size
= linear_size
;
454 image
->row_pitch
= linear_stride
;
461 wsi_destroy_image(chain
, image
);
467 wsi_destroy_image(const struct wsi_swapchain
*chain
,
468 struct wsi_image
*image
)
470 const struct wsi_device
*wsi
= chain
->wsi
;
472 if (image
->prime
.blit_cmd_buffers
) {
473 for (uint32_t i
= 0; i
< wsi
->queue_family_count
; i
++) {
474 wsi
->FreeCommandBuffers(chain
->device
, chain
->cmd_pools
[i
],
475 1, &image
->prime
.blit_cmd_buffers
[i
]);
477 vk_free(&chain
->alloc
, image
->prime
.blit_cmd_buffers
);
480 wsi
->FreeMemory(chain
->device
, image
->memory
, &chain
->alloc
);
481 wsi
->DestroyImage(chain
->device
, image
->image
, &chain
->alloc
);
482 wsi
->FreeMemory(chain
->device
, image
->prime
.memory
, &chain
->alloc
);
483 wsi
->DestroyBuffer(chain
->device
, image
->prime
.buffer
, &chain
->alloc
);
487 wsi_prime_image_blit_to_linear(const struct wsi_swapchain
*chain
,
488 struct wsi_image
*image
,
490 uint32_t waitSemaphoreCount
,
491 const VkSemaphore
*pWaitSemaphores
)
493 uint32_t queue_family
= chain
->wsi
->queue_get_family_index(queue
);
495 VkPipelineStageFlags stage_flags
= VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT
;
496 const VkSubmitInfo submit_info
= {
497 .sType
= VK_STRUCTURE_TYPE_SUBMIT_INFO
,
499 .waitSemaphoreCount
= waitSemaphoreCount
,
500 .pWaitSemaphores
= pWaitSemaphores
,
501 .pWaitDstStageMask
= &stage_flags
,
502 .commandBufferCount
= 1,
503 .pCommandBuffers
= &image
->prime
.blit_cmd_buffers
[queue_family
],
504 .signalSemaphoreCount
= 0,
505 .pSignalSemaphores
= NULL
,
507 return chain
->wsi
->QueueSubmit(queue
, 1, &submit_info
, VK_NULL_HANDLE
);
511 wsi_common_queue_present(const struct wsi_device
*wsi
,
514 int queue_family_index
,
515 const VkPresentInfoKHR
*pPresentInfo
)
517 VkResult final_result
= VK_SUCCESS
;
519 const VkPresentRegionsKHR
*regions
=
520 vk_find_struct_const(pPresentInfo
->pNext
, PRESENT_REGIONS_KHR
);
522 for (uint32_t i
= 0; i
< pPresentInfo
->swapchainCount
; i
++) {
523 WSI_FROM_HANDLE(wsi_swapchain
, swapchain
, pPresentInfo
->pSwapchains
[i
]);
526 if (swapchain
->fences
[0] == VK_NULL_HANDLE
) {
527 const VkFenceCreateInfo fence_info
= {
528 .sType
= VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
,
532 result
= wsi
->CreateFence(device
, &fence_info
,
534 &swapchain
->fences
[0]);
535 if (result
!= VK_SUCCESS
)
538 wsi
->ResetFences(device
, 1, &swapchain
->fences
[0]);
541 VkSubmitInfo submit_info
= {
542 .sType
= VK_STRUCTURE_TYPE_SUBMIT_INFO
,
545 VkPipelineStageFlags
*stage_flags
= NULL
;
547 /* We only need/want to wait on semaphores once. After that, we're
548 * guaranteed ordering since it all happens on the same queue.
550 submit_info
.waitSemaphoreCount
= pPresentInfo
->waitSemaphoreCount
,
551 submit_info
.pWaitSemaphores
= pPresentInfo
->pWaitSemaphores
,
553 /* Set up the pWaitDstStageMasks */
554 stage_flags
= vk_alloc(&swapchain
->alloc
,
555 sizeof(VkPipelineStageFlags
) *
556 pPresentInfo
->waitSemaphoreCount
,
558 VK_SYSTEM_ALLOCATION_SCOPE_COMMAND
);
560 result
= VK_ERROR_OUT_OF_HOST_MEMORY
;
563 for (uint32_t s
= 0; s
< pPresentInfo
->waitSemaphoreCount
; s
++)
564 stage_flags
[s
] = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT
;
566 submit_info
.pWaitDstStageMask
= stage_flags
;
568 result
= wsi
->QueueSubmit(queue
, 1, &submit_info
, swapchain
->fences
[0]);
569 vk_free(&swapchain
->alloc
, stage_flags
);
570 if (result
!= VK_SUCCESS
)
573 const VkPresentRegionKHR
*region
= NULL
;
574 if (regions
&& regions
->pRegions
)
575 region
= ®ions
->pRegions
[i
];
577 result
= swapchain
->queue_present(swapchain
,
579 pPresentInfo
->waitSemaphoreCount
,
580 pPresentInfo
->pWaitSemaphores
,
581 pPresentInfo
->pImageIndices
[i
],
583 if (result
!= VK_SUCCESS
)
586 VkFence last
= swapchain
->fences
[2];
587 swapchain
->fences
[2] = swapchain
->fences
[1];
588 swapchain
->fences
[1] = swapchain
->fences
[0];
589 swapchain
->fences
[0] = last
;
591 if (last
!= VK_NULL_HANDLE
) {
592 wsi
->WaitForFences(device
, 1, &last
, true, 1);
596 if (pPresentInfo
->pResults
!= NULL
)
597 pPresentInfo
->pResults
[i
] = result
;
599 /* Let the final result be our first unsuccessful result */
600 if (final_result
== VK_SUCCESS
)
601 final_result
= result
;