1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2016-2017 - Hans-Kristian Arntzen
3 * Copyright (C) 2016-2019 - Brad Parker
4 *
5 * RetroArch is free software: you can redistribute it and/or modify it under the terms
6 * of the GNU General Public License as published by the Free Software Found-
7 * ation, either version 3 of the License, or (at your option) any later version.
8 *
9 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with RetroArch.
14 * If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include <retro_assert.h>
18 #include <dynamic/dylib.h>
19 #include <string/stdstring.h>
20
21 #ifdef HAVE_CONFIG_H
22 #include "../../config.h"
23 #endif
24
25 #ifdef HAVE_X11
26 #ifdef HAVE_XCB
27 #include <X11/Xlib-xcb.h>
28 #endif
29 #endif
30
31 #include "vulkan_common.h"
32 #include <retro_timers.h>
33 #include "../../configuration.h"
34 #include "../include/vulkan/vulkan.h"
35 #include <retro_assert.h>
36 #include "vksym.h"
37 #include <libretro_vulkan.h>
38 #include <retro_math.h>
39 #include <lists/string_list.h>
40
41 #define VENDOR_ID_AMD 0x1002
42 #define VENDOR_ID_NV 0x10DE
43 #define VENDOR_ID_INTEL 0x8086
44
45 #if defined(_WIN32)
46 #define VULKAN_EMULATE_MAILBOX
47 #endif
48
49 /* TODO/FIXME - static globals */
50 static dylib_t vulkan_library;
51 static VkInstance cached_instance_vk;
52 static VkDevice cached_device_vk;
53 static retro_vulkan_destroy_device_t cached_destroy_device_vk;
54
55 #if 0
56 #define WSI_HARDENING_TEST
57 #endif
58
59 #ifdef WSI_HARDENING_TEST
60 static unsigned wsi_harden_counter = 0;
61 static unsigned wsi_harden_counter2 = 0;
62
trigger_spurious_error_vkresult(VkResult * res)63 static void trigger_spurious_error_vkresult(VkResult *res)
64 {
65 ++wsi_harden_counter;
66 if ((wsi_harden_counter & 15) == 12)
67 *res = VK_ERROR_OUT_OF_DATE_KHR;
68 else if ((wsi_harden_counter & 31) == 13)
69 *res = VK_ERROR_OUT_OF_DATE_KHR;
70 else if ((wsi_harden_counter & 15) == 6)
71 *res = VK_ERROR_SURFACE_LOST_KHR;
72 }
73
trigger_spurious_error(void)74 static bool trigger_spurious_error(void)
75 {
76 ++wsi_harden_counter2;
77 return ((wsi_harden_counter2 & 15) == 9) || ((wsi_harden_counter2 & 15) == 10);
78 }
79 #endif
80
81 #ifdef VULKAN_DEBUG
vulkan_debug_cb(VkDebugReportFlagsEXT flags,VkDebugReportObjectTypeEXT objectType,uint64_t object,size_t location,int32_t messageCode,const char * pLayerPrefix,const char * pMessage,void * pUserData)82 static VKAPI_ATTR VkBool32 VKAPI_CALL vulkan_debug_cb(
83 VkDebugReportFlagsEXT flags,
84 VkDebugReportObjectTypeEXT objectType,
85 uint64_t object,
86 size_t location,
87 int32_t messageCode,
88 const char *pLayerPrefix,
89 const char *pMessage,
90 void *pUserData)
91 {
92 (void)objectType;
93 (void)object;
94 (void)location;
95 (void)messageCode;
96 (void)pUserData;
97
98 if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
99 {
100 RARCH_ERR("[Vulkan]: Error: %s: %s\n",
101 pLayerPrefix, pMessage);
102 }
103 #if 0
104 else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
105 {
106 RARCH_WARN("[Vulkan]: Warning: %s: %s\n",
107 pLayerPrefix, pMessage);
108 }
109 else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
110 {
111 RARCH_LOG("[Vulkan]: Performance warning: %s: %s\n",
112 pLayerPrefix, pMessage);
113 }
114 else
115 {
116 RARCH_LOG("[Vulkan]: Information: %s: %s\n",
117 pLayerPrefix, pMessage);
118 }
119 #endif
120
121 return VK_FALSE;
122 }
123 #endif
124
vulkan_emulated_mailbox_deinit(struct vulkan_emulated_mailbox * mailbox)125 static void vulkan_emulated_mailbox_deinit(
126 struct vulkan_emulated_mailbox *mailbox)
127 {
128 if (mailbox->thread)
129 {
130 slock_lock(mailbox->lock);
131 mailbox->dead = true;
132 scond_signal(mailbox->cond);
133 slock_unlock(mailbox->lock);
134 sthread_join(mailbox->thread);
135 }
136
137 if (mailbox->lock)
138 slock_free(mailbox->lock);
139 if (mailbox->cond)
140 scond_free(mailbox->cond);
141
142 memset(mailbox, 0, sizeof(*mailbox));
143 }
144
vulkan_emulated_mailbox_acquire_next_image(struct vulkan_emulated_mailbox * mailbox,unsigned * index)145 static VkResult vulkan_emulated_mailbox_acquire_next_image(
146 struct vulkan_emulated_mailbox *mailbox,
147 unsigned *index)
148 {
149 VkResult res = VK_TIMEOUT;
150
151 slock_lock(mailbox->lock);
152
153 if (!mailbox->has_pending_request)
154 {
155 mailbox->request_acquire = true;
156 scond_signal(mailbox->cond);
157 }
158
159 mailbox->has_pending_request = true;
160
161 if (mailbox->acquired)
162 {
163 res = mailbox->result;
164 *index = mailbox->index;
165 mailbox->has_pending_request = false;
166 mailbox->acquired = false;
167 }
168
169 slock_unlock(mailbox->lock);
170 return res;
171 }
172
vulkan_emulated_mailbox_acquire_next_image_blocking(struct vulkan_emulated_mailbox * mailbox,unsigned * index)173 static VkResult vulkan_emulated_mailbox_acquire_next_image_blocking(
174 struct vulkan_emulated_mailbox *mailbox,
175 unsigned *index)
176 {
177 VkResult res;
178
179 slock_lock(mailbox->lock);
180
181 if (!mailbox->has_pending_request)
182 {
183 mailbox->request_acquire = true;
184 scond_signal(mailbox->cond);
185 }
186
187 mailbox->has_pending_request = true;
188
189 while (!mailbox->acquired)
190 scond_wait(mailbox->cond, mailbox->lock);
191
192 res = mailbox->result;
193 if (res == VK_SUCCESS)
194 *index = mailbox->index;
195 mailbox->has_pending_request = false;
196 mailbox->acquired = false;
197
198 slock_unlock(mailbox->lock);
199 return res;
200 }
201
vulkan_emulated_mailbox_loop(void * userdata)202 static void vulkan_emulated_mailbox_loop(void *userdata)
203 {
204 VkFence fence;
205 VkFenceCreateInfo info;
206 struct vulkan_emulated_mailbox *mailbox =
207 (struct vulkan_emulated_mailbox*)userdata;
208
209 if (!mailbox)
210 return;
211
212 info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
213 info.pNext = NULL;
214 info.flags = 0;
215
216 vkCreateFence(mailbox->device, &info, NULL, &fence);
217
218 for (;;)
219 {
220 slock_lock(mailbox->lock);
221 while (!mailbox->dead && !mailbox->request_acquire)
222 scond_wait(mailbox->cond, mailbox->lock);
223
224 if (mailbox->dead)
225 {
226 slock_unlock(mailbox->lock);
227 break;
228 }
229
230 mailbox->request_acquire = false;
231 slock_unlock(mailbox->lock);
232
233 mailbox->result = vkAcquireNextImageKHR(
234 mailbox->device, mailbox->swapchain, UINT64_MAX,
235 VK_NULL_HANDLE, fence, &mailbox->index);
236
237 /* VK_SUBOPTIMAL_KHR can be returned on Android 10
238 * when prerotate is not dealt with.
239 * This is not an error we need to care about,
240 * and we'll treat it as SUCCESS. */
241 if (mailbox->result == VK_SUBOPTIMAL_KHR)
242 mailbox->result = VK_SUCCESS;
243
244 if (mailbox->result == VK_SUCCESS)
245 vkWaitForFences(mailbox->device, 1,
246 &fence, true, UINT64_MAX);
247 vkResetFences(mailbox->device, 1, &fence);
248
249 if (mailbox->result == VK_SUCCESS)
250 {
251 slock_lock(mailbox->lock);
252 mailbox->acquired = true;
253 scond_signal(mailbox->cond);
254 slock_unlock(mailbox->lock);
255 }
256 }
257
258 vkDestroyFence(mailbox->device, fence, NULL);
259 }
260
vulkan_emulated_mailbox_init(struct vulkan_emulated_mailbox * mailbox,VkDevice device,VkSwapchainKHR swapchain)261 static bool vulkan_emulated_mailbox_init(
262 struct vulkan_emulated_mailbox *mailbox,
263 VkDevice device,
264 VkSwapchainKHR swapchain)
265 {
266 memset(mailbox, 0, sizeof(*mailbox));
267 mailbox->device = device;
268 mailbox->swapchain = swapchain;
269
270 mailbox->cond = scond_new();
271 if (!mailbox->cond)
272 return false;
273 mailbox->lock = slock_new();
274 if (!mailbox->lock)
275 return false;
276 mailbox->thread = sthread_create(vulkan_emulated_mailbox_loop, mailbox);
277 if (!mailbox->thread)
278 return false;
279 return true;
280 }
281
vulkan_find_memory_type(const VkPhysicalDeviceMemoryProperties * mem_props,uint32_t device_reqs,uint32_t host_reqs)282 uint32_t vulkan_find_memory_type(
283 const VkPhysicalDeviceMemoryProperties *mem_props,
284 uint32_t device_reqs, uint32_t host_reqs)
285 {
286 uint32_t i;
287 for (i = 0; i < VK_MAX_MEMORY_TYPES; i++)
288 {
289 if ((device_reqs & (1u << i)) &&
290 (mem_props->memoryTypes[i].propertyFlags & host_reqs) == host_reqs)
291 return i;
292 }
293
294 RARCH_ERR("[Vulkan]: Failed to find valid memory type. This should never happen.");
295 abort();
296 }
297
vulkan_find_memory_type_fallback(const VkPhysicalDeviceMemoryProperties * mem_props,uint32_t device_reqs,uint32_t host_reqs_first,uint32_t host_reqs_second)298 uint32_t vulkan_find_memory_type_fallback(
299 const VkPhysicalDeviceMemoryProperties *mem_props,
300 uint32_t device_reqs, uint32_t host_reqs_first,
301 uint32_t host_reqs_second)
302 {
303 uint32_t i;
304 for (i = 0; i < VK_MAX_MEMORY_TYPES; i++)
305 {
306 if ((device_reqs & (1u << i)) &&
307 (mem_props->memoryTypes[i].propertyFlags & host_reqs_first) == host_reqs_first)
308 return i;
309 }
310
311 if (host_reqs_first == 0)
312 {
313 RARCH_ERR("[Vulkan]: Failed to find valid memory type. This should never happen.");
314 abort();
315 }
316
317 return vulkan_find_memory_type_fallback(mem_props,
318 device_reqs, host_reqs_second, 0);
319 }
320
321 #ifdef VULKAN_DEBUG_TEXTURE_ALLOC
322 static VkImage vk_images[4 * 1024];
323 static unsigned vk_count;
324 static unsigned track_seq;
325
vulkan_log_textures(void)326 void vulkan_log_textures(void)
327 {
328 unsigned i;
329 for (i = 0; i < vk_count; i++)
330 {
331 RARCH_WARN("[Vulkan]: Found leaked texture %llu.\n",
332 (unsigned long long)vk_images[i]);
333 }
334 vk_count = 0;
335 }
336
vulkan_track_alloc(VkImage image)337 static void vulkan_track_alloc(VkImage image)
338 {
339 vk_images[vk_count++] = image;
340 RARCH_LOG("[Vulkan]: Alloc %llu (%u).\n",
341 (unsigned long long)image, track_seq);
342 track_seq++;
343 }
344
vulkan_track_dealloc(VkImage image)345 static void vulkan_track_dealloc(VkImage image)
346 {
347 unsigned i;
348 for (i = 0; i < vk_count; i++)
349 {
350 if (image == vk_images[i])
351 {
352 vk_count--;
353 memmove(vk_images + i, vk_images + 1 + i,
354 sizeof(VkImage) * (vk_count - i));
355 return;
356 }
357 }
358 retro_assert(0 && "Couldn't find VkImage in dealloc!");
359 }
360 #endif
361
vulkan_num_miplevels(unsigned width,unsigned height)362 static unsigned vulkan_num_miplevels(unsigned width, unsigned height)
363 {
364 unsigned size = MAX(width, height);
365 unsigned levels = 0;
366 while (size)
367 {
368 levels++;
369 size >>= 1;
370 }
371 return levels;
372 }
373
vulkan_create_texture(vk_t * vk,struct vk_texture * old,unsigned width,unsigned height,VkFormat format,const void * initial,const VkComponentMapping * swizzle,enum vk_texture_type type)374 struct vk_texture vulkan_create_texture(vk_t *vk,
375 struct vk_texture *old,
376 unsigned width, unsigned height,
377 VkFormat format,
378 const void *initial,
379 const VkComponentMapping *swizzle,
380 enum vk_texture_type type)
381 {
382 unsigned i;
383 struct vk_texture tex;
384 VkMemoryRequirements mem_reqs;
385 VkSubresourceLayout layout;
386 VkDevice device = vk->context->device;
387 VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
388 VkBufferCreateInfo buffer_info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
389 VkImageViewCreateInfo view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
390 VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
391 VkImageSubresource subresource = { VK_IMAGE_ASPECT_COLOR_BIT };
392 VkCommandBufferAllocateInfo cmd_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
393 VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
394 VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
395
396 memset(&tex, 0, sizeof(tex));
397
398 info.imageType = VK_IMAGE_TYPE_2D;
399 info.format = format;
400 info.extent.width = width;
401 info.extent.height = height;
402 info.extent.depth = 1;
403 info.arrayLayers = 1;
404 info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
405 info.mipLevels = 1;
406 info.samples = VK_SAMPLE_COUNT_1_BIT;
407
408 buffer_info.size = width * height * vulkan_format_to_bpp(format);
409 buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
410
411 if (type == VULKAN_TEXTURE_STREAMED)
412 {
413 VkFormatProperties format_properties;
414 const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
415 VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
416
417 vkGetPhysicalDeviceFormatProperties(
418 vk->context->gpu, format, &format_properties);
419
420 if ((format_properties.linearTilingFeatures & required) != required)
421 {
422 RARCH_LOG("[Vulkan]: GPU does not support using linear images as textures. Falling back to copy path.\n");
423 type = VULKAN_TEXTURE_STAGING;
424 }
425 }
426
427 switch (type)
428 {
429 case VULKAN_TEXTURE_STATIC:
430 /* For simplicity, always build mipmaps for
431 * static textures, samplers can be used to enable it dynamically.
432 */
433 info.mipLevels = vulkan_num_miplevels(width, height);
434 tex.mipmap = true;
435 retro_assert(initial && "Static textures must have initial data.\n");
436 info.tiling = VK_IMAGE_TILING_OPTIMAL;
437 info.usage = VK_IMAGE_USAGE_SAMPLED_BIT |
438 VK_IMAGE_USAGE_TRANSFER_DST_BIT |
439 VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
440 info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
441 break;
442
443 case VULKAN_TEXTURE_DYNAMIC:
444 retro_assert(!initial && "Dynamic textures must not have initial data.\n");
445 info.tiling = VK_IMAGE_TILING_OPTIMAL;
446 info.usage = VK_IMAGE_USAGE_SAMPLED_BIT |
447 VK_IMAGE_USAGE_TRANSFER_DST_BIT |
448 VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
449 info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
450 break;
451
452 case VULKAN_TEXTURE_STREAMED:
453 info.usage = VK_IMAGE_USAGE_SAMPLED_BIT |
454 VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
455 info.tiling = VK_IMAGE_TILING_LINEAR;
456 info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
457 break;
458
459 case VULKAN_TEXTURE_STAGING:
460 buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
461 info.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
462 info.tiling = VK_IMAGE_TILING_LINEAR;
463 break;
464
465 case VULKAN_TEXTURE_READBACK:
466 buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
467 info.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
468 info.tiling = VK_IMAGE_TILING_LINEAR;
469 break;
470 }
471
472 if (type != VULKAN_TEXTURE_STAGING && type != VULKAN_TEXTURE_READBACK)
473 {
474 vkCreateImage(device, &info, NULL, &tex.image);
475 #if 0
476 vulkan_track_alloc(tex.image);
477 #endif
478 vkGetImageMemoryRequirements(device, tex.image, &mem_reqs);
479 }
480 else
481 {
482 /* Linear staging textures are not guaranteed to be supported,
483 * use buffers instead. */
484 vkCreateBuffer(device, &buffer_info, NULL, &tex.buffer);
485 vkGetBufferMemoryRequirements(device, tex.buffer, &mem_reqs);
486 }
487 alloc.allocationSize = mem_reqs.size;
488
489 switch (type)
490 {
491 case VULKAN_TEXTURE_STATIC:
492 case VULKAN_TEXTURE_DYNAMIC:
493 alloc.memoryTypeIndex = vulkan_find_memory_type_fallback(
494 &vk->context->memory_properties,
495 mem_reqs.memoryTypeBits,
496 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0);
497 break;
498
499 default:
500 /* Try to find a memory type which is cached, even if it means manual cache management. */
501 alloc.memoryTypeIndex = vulkan_find_memory_type_fallback(
502 &vk->context->memory_properties,
503 mem_reqs.memoryTypeBits,
504 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
505 VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
506 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
507 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
508
509 tex.need_manual_cache_management =
510 (vk->context->memory_properties.memoryTypes[alloc.memoryTypeIndex].propertyFlags &
511 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0;
512
513 /* If the texture is STREAMED and it's not DEVICE_LOCAL, we expect to hit a slower path,
514 * so fallback to copy path. */
515 if (type == VULKAN_TEXTURE_STREAMED &&
516 (vk->context->memory_properties.memoryTypes[alloc.memoryTypeIndex].propertyFlags &
517 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0)
518 {
519 /* Recreate texture but for STAGING this time ... */
520 #ifdef VULKAN_DEBUG
521 RARCH_LOG("[Vulkan]: GPU supports linear images as textures, but not DEVICE_LOCAL. Falling back to copy path.\n");
522 #endif
523 type = VULKAN_TEXTURE_STAGING;
524 vkDestroyImage(device, tex.image, NULL);
525 tex.image = (VkImage)NULL;
526 info.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
527
528 buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
529 vkCreateBuffer(device, &buffer_info, NULL, &tex.buffer);
530 vkGetBufferMemoryRequirements(device, tex.buffer, &mem_reqs);
531
532 alloc.allocationSize = mem_reqs.size;
533 alloc.memoryTypeIndex = vulkan_find_memory_type_fallback(
534 &vk->context->memory_properties,
535 mem_reqs.memoryTypeBits,
536 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
537 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
538 VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
539 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
540 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
541 }
542 break;
543 }
544
545 /* We're not reusing the objects themselves. */
546 if (old)
547 {
548 if (old->view != VK_NULL_HANDLE)
549 vkDestroyImageView(vk->context->device, old->view, NULL);
550 if (old->image != VK_NULL_HANDLE)
551 {
552 vkDestroyImage(vk->context->device, old->image, NULL);
553 #ifdef VULKAN_DEBUG_TEXTURE_ALLOC
554 vulkan_track_dealloc(old->image);
555 #endif
556 }
557 if (old->buffer != VK_NULL_HANDLE)
558 vkDestroyBuffer(vk->context->device, old->buffer, NULL);
559 }
560
561 /* We can pilfer the old memory and move it over to the new texture. */
562 if (old &&
563 old->memory_size >= mem_reqs.size &&
564 old->memory_type == alloc.memoryTypeIndex)
565 {
566 tex.memory = old->memory;
567 tex.memory_size = old->memory_size;
568 tex.memory_type = old->memory_type;
569
570 if (old->mapped)
571 vkUnmapMemory(device, old->memory);
572
573 old->memory = VK_NULL_HANDLE;
574 }
575 else
576 {
577 vkAllocateMemory(device, &alloc, NULL, &tex.memory);
578 tex.memory_size = alloc.allocationSize;
579 tex.memory_type = alloc.memoryTypeIndex;
580 }
581
582 if (old)
583 {
584 if (old->memory != VK_NULL_HANDLE)
585 vkFreeMemory(device, old->memory, NULL);
586 memset(old, 0, sizeof(*old));
587 }
588
589 if (tex.image)
590 vkBindImageMemory(device, tex.image, tex.memory, 0);
591 if (tex.buffer)
592 vkBindBufferMemory(device, tex.buffer, tex.memory, 0);
593
594 if (type != VULKAN_TEXTURE_STAGING && type != VULKAN_TEXTURE_READBACK)
595 {
596 view.image = tex.image;
597 view.viewType = VK_IMAGE_VIEW_TYPE_2D;
598 view.format = format;
599 if (swizzle)
600 view.components = *swizzle;
601 else
602 {
603 view.components.r = VK_COMPONENT_SWIZZLE_R;
604 view.components.g = VK_COMPONENT_SWIZZLE_G;
605 view.components.b = VK_COMPONENT_SWIZZLE_B;
606 view.components.a = VK_COMPONENT_SWIZZLE_A;
607 }
608 view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
609 view.subresourceRange.levelCount = info.mipLevels;
610 view.subresourceRange.layerCount = 1;
611
612 vkCreateImageView(device, &view, NULL, &tex.view);
613 }
614 else
615 tex.view = VK_NULL_HANDLE;
616
617 if (tex.image && info.tiling == VK_IMAGE_TILING_LINEAR)
618 vkGetImageSubresourceLayout(device, tex.image, &subresource, &layout);
619 else if (tex.buffer)
620 {
621 layout.offset = 0;
622 layout.size = buffer_info.size;
623 layout.rowPitch = width * vulkan_format_to_bpp(format);
624 }
625 else
626 memset(&layout, 0, sizeof(layout));
627
628 tex.stride = layout.rowPitch;
629 tex.offset = layout.offset;
630 tex.size = layout.size;
631 tex.layout = info.initialLayout;
632
633 tex.width = width;
634 tex.height = height;
635 tex.format = format;
636 tex.type = type;
637
638 if (initial)
639 {
640 switch (type)
641 {
642 case VULKAN_TEXTURE_STREAMED:
643 case VULKAN_TEXTURE_STAGING:
644 {
645 unsigned y;
646 uint8_t *dst = NULL;
647 const uint8_t *src = NULL;
648 void *ptr = NULL;
649 unsigned bpp = vulkan_format_to_bpp(tex.format);
650 unsigned stride = tex.width * bpp;
651
652 vkMapMemory(device, tex.memory, tex.offset, tex.size, 0, &ptr);
653
654 dst = (uint8_t*)ptr;
655 src = (const uint8_t*)initial;
656 for (y = 0; y < tex.height; y++, dst += tex.stride, src += stride)
657 memcpy(dst, src, width * bpp);
658
659 if ( tex.need_manual_cache_management &&
660 tex.memory != VK_NULL_HANDLE)
661 VULKAN_SYNC_TEXTURE_TO_GPU(vk->context->device, tex.memory);
662 vkUnmapMemory(device, tex.memory);
663 }
664 break;
665 case VULKAN_TEXTURE_STATIC:
666 {
667 VkBufferImageCopy region;
668 VkCommandBuffer staging;
669 struct vk_texture tmp = vulkan_create_texture(vk, NULL,
670 width, height, format, initial, NULL, VULKAN_TEXTURE_STAGING);
671
672 cmd_info.commandPool = vk->staging_pool;
673 cmd_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
674 cmd_info.commandBufferCount = 1;
675
676 vkAllocateCommandBuffers(vk->context->device,
677 &cmd_info, &staging);
678
679 begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
680
681 vkBeginCommandBuffer(staging, &begin_info);
682
683 /* If doing mipmapping on upload, keep in general
684 * so we can easily do transfers to
685 * and transfers from the images without having to
686 * mess around with lots of extra transitions at
687 * per-level granularity.
688 */
689 VULKAN_IMAGE_LAYOUT_TRANSITION(
690 staging,
691 tex.image,
692 VK_IMAGE_LAYOUT_UNDEFINED,
693 tex.mipmap
694 ? VK_IMAGE_LAYOUT_GENERAL
695 : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
696 0, VK_ACCESS_TRANSFER_WRITE_BIT,
697 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
698 VK_PIPELINE_STAGE_TRANSFER_BIT);
699
700 memset(®ion, 0, sizeof(region));
701 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
702 region.imageSubresource.layerCount = 1;
703 region.imageExtent.width = width;
704 region.imageExtent.height = height;
705 region.imageExtent.depth = 1;
706
707 vkCmdCopyBufferToImage(staging,
708 tmp.buffer,
709 tex.image,
710 tex.mipmap
711 ? VK_IMAGE_LAYOUT_GENERAL
712 : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
713 1, ®ion);
714
715 if (tex.mipmap)
716 {
717 for (i = 1; i < info.mipLevels; i++)
718 {
719 VkImageBlit blit_region;
720 unsigned src_width = MAX(width >> (i - 1), 1);
721 unsigned src_height = MAX(height >> (i - 1), 1);
722 unsigned target_width = MAX(width >> i, 1);
723 unsigned target_height = MAX(height >> i, 1);
724 memset(&blit_region, 0, sizeof(blit_region));
725
726 blit_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
727 blit_region.srcSubresource.mipLevel = i - 1;
728 blit_region.srcSubresource.baseArrayLayer = 0;
729 blit_region.srcSubresource.layerCount = 1;
730 blit_region.dstSubresource = blit_region.srcSubresource;
731 blit_region.dstSubresource.mipLevel = i;
732 blit_region.srcOffsets[1].x = src_width;
733 blit_region.srcOffsets[1].y = src_height;
734 blit_region.srcOffsets[1].z = 1;
735 blit_region.dstOffsets[1].x = target_width;
736 blit_region.dstOffsets[1].y = target_height;
737 blit_region.dstOffsets[1].z = 1;
738
739 /* Only injects execution and memory barriers,
740 * not actual transition. */
741 VULKAN_IMAGE_LAYOUT_TRANSITION(
742 staging,
743 tex.image,
744 VK_IMAGE_LAYOUT_GENERAL,
745 VK_IMAGE_LAYOUT_GENERAL,
746 VK_ACCESS_TRANSFER_WRITE_BIT,
747 VK_ACCESS_TRANSFER_READ_BIT,
748 VK_PIPELINE_STAGE_TRANSFER_BIT,
749 VK_PIPELINE_STAGE_TRANSFER_BIT);
750
751 vkCmdBlitImage(
752 staging,
753 tex.image,
754 VK_IMAGE_LAYOUT_GENERAL,
755 tex.image,
756 VK_IMAGE_LAYOUT_GENERAL,
757 1,
758 &blit_region,
759 VK_FILTER_LINEAR);
760 }
761
762 /* Complete our texture. */
763 VULKAN_IMAGE_LAYOUT_TRANSITION(
764 staging,
765 tex.image,
766 VK_IMAGE_LAYOUT_GENERAL,
767 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
768 VK_ACCESS_TRANSFER_WRITE_BIT,
769 VK_ACCESS_SHADER_READ_BIT,
770 VK_PIPELINE_STAGE_TRANSFER_BIT,
771 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
772 }
773 else
774 {
775 VULKAN_IMAGE_LAYOUT_TRANSITION(
776 staging,
777 tex.image,
778 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
779 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
780 VK_ACCESS_TRANSFER_WRITE_BIT,
781 VK_ACCESS_SHADER_READ_BIT,
782 VK_PIPELINE_STAGE_TRANSFER_BIT,
783 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
784 }
785
786 vkEndCommandBuffer(staging);
787 submit_info.commandBufferCount = 1;
788 submit_info.pCommandBuffers = &staging;
789
790 #ifdef HAVE_THREADS
791 slock_lock(vk->context->queue_lock);
792 #endif
793 vkQueueSubmit(vk->context->queue,
794 1, &submit_info, VK_NULL_HANDLE);
795
796 /* TODO: Very crude, but texture uploads only happen
797 * during init, so waiting for GPU to complete transfer
798 * and blocking isn't a big deal. */
799 vkQueueWaitIdle(vk->context->queue);
800 #ifdef HAVE_THREADS
801 slock_unlock(vk->context->queue_lock);
802 #endif
803
804 vkFreeCommandBuffers(vk->context->device,
805 vk->staging_pool, 1, &staging);
806 vulkan_destroy_texture(
807 vk->context->device, &tmp);
808 tex.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
809 }
810 break;
811 case VULKAN_TEXTURE_DYNAMIC:
812 case VULKAN_TEXTURE_READBACK:
813 /* TODO/FIXME - stubs */
814 break;
815 }
816 }
817
818 return tex;
819 }
820
vulkan_destroy_texture(VkDevice device,struct vk_texture * tex)821 void vulkan_destroy_texture(
822 VkDevice device,
823 struct vk_texture *tex)
824 {
825 if (tex->mapped)
826 vkUnmapMemory(device, tex->memory);
827 if (tex->view)
828 vkDestroyImageView(device, tex->view, NULL);
829 if (tex->image)
830 vkDestroyImage(device, tex->image, NULL);
831 if (tex->buffer)
832 vkDestroyBuffer(device, tex->buffer, NULL);
833 if (tex->memory)
834 vkFreeMemory(device, tex->memory, NULL);
835
836 #ifdef VULKAN_DEBUG_TEXTURE_ALLOC
837 if (tex->image)
838 vulkan_track_dealloc(tex->image);
839 #endif
840 tex->type = VULKAN_TEXTURE_STREAMED;
841 tex->default_smooth = false;
842 tex->need_manual_cache_management = false;
843 tex->mipmap = false;
844 tex->memory_type = 0;
845 tex->width = 0;
846 tex->height = 0;
847 tex->offset = 0;
848 tex->stride = 0;
849 tex->size = 0;
850 tex->mapped = NULL;
851 tex->image = VK_NULL_HANDLE;
852 tex->view = VK_NULL_HANDLE;
853 tex->memory = VK_NULL_HANDLE;
854 tex->buffer = VK_NULL_HANDLE;
855 tex->format = VK_FORMAT_UNDEFINED;
856 tex->memory_size = 0;
857 tex->layout = VK_IMAGE_LAYOUT_UNDEFINED;
858 }
859
vulkan_write_quad_descriptors(VkDevice device,VkDescriptorSet set,VkBuffer buffer,VkDeviceSize offset,VkDeviceSize range,const struct vk_texture * texture,VkSampler sampler)860 static void vulkan_write_quad_descriptors(
861 VkDevice device,
862 VkDescriptorSet set,
863 VkBuffer buffer,
864 VkDeviceSize offset,
865 VkDeviceSize range,
866 const struct vk_texture *texture,
867 VkSampler sampler)
868 {
869 VkWriteDescriptorSet write;
870 VkDescriptorBufferInfo buffer_info;
871
872 buffer_info.buffer = buffer;
873 buffer_info.offset = offset;
874 buffer_info.range = range;
875
876 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
877 write.pNext = NULL;
878 write.dstSet = set;
879 write.dstBinding = 0;
880 write.dstArrayElement = 0;
881 write.descriptorCount = 1;
882 write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
883 write.pImageInfo = NULL;
884 write.pBufferInfo = &buffer_info;
885 write.pTexelBufferView = NULL;
886 vkUpdateDescriptorSets(device, 1, &write, 0, NULL);
887
888 if (texture)
889 {
890 VkDescriptorImageInfo image_info;
891
892 image_info.sampler = sampler;
893 image_info.imageView = texture->view;
894 image_info.imageLayout = texture->layout;
895
896 write.dstSet = set;
897 write.dstBinding = 1;
898 write.descriptorCount = 1;
899 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
900 write.pImageInfo = &image_info;
901 vkUpdateDescriptorSets(device, 1, &write, 0, NULL);
902 }
903 }
904
vulkan_transition_texture(vk_t * vk,VkCommandBuffer cmd,struct vk_texture * texture)905 void vulkan_transition_texture(vk_t *vk, VkCommandBuffer cmd, struct vk_texture *texture)
906 {
907 if (!texture->image)
908 return;
909
910 /* Transition to GENERAL layout for linear streamed textures.
911 * We're using linear textures here, so only
912 * GENERAL layout is supported.
913 * If we're already in GENERAL, add a host -> shader read memory barrier
914 * to invalidate texture caches.
915 */
916 if (texture->layout != VK_IMAGE_LAYOUT_PREINITIALIZED &&
917 texture->layout != VK_IMAGE_LAYOUT_GENERAL)
918 return;
919
920 switch (texture->type)
921 {
922 case VULKAN_TEXTURE_STREAMED:
923 VULKAN_IMAGE_LAYOUT_TRANSITION(cmd, texture->image,
924 texture->layout, VK_IMAGE_LAYOUT_GENERAL,
925 VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
926 VK_PIPELINE_STAGE_HOST_BIT,
927 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
928 break;
929
930 default:
931 retro_assert(0 && "Attempting to transition invalid texture type.\n");
932 break;
933 }
934 texture->layout = VK_IMAGE_LAYOUT_GENERAL;
935 }
936
vulkan_check_dynamic_state(vk_t * vk)937 static void vulkan_check_dynamic_state(vk_t *vk)
938 {
939 VkRect2D sci;
940
941 if (vk->tracker.use_scissor)
942 sci = vk->tracker.scissor;
943 else
944 {
945 /* No scissor -> viewport */
946 sci.offset.x = vk->vp.x;
947 sci.offset.y = vk->vp.y;
948 sci.extent.width = vk->vp.width;
949 sci.extent.height = vk->vp.height;
950 }
951
952 vkCmdSetViewport(vk->cmd, 0, 1, &vk->vk_vp);
953 vkCmdSetScissor (vk->cmd, 0, 1, &sci);
954
955 vk->tracker.dirty &= ~VULKAN_DIRTY_DYNAMIC_BIT;
956 }
957
vulkan_draw_triangles(vk_t * vk,const struct vk_draw_triangles * call)958 void vulkan_draw_triangles(vk_t *vk, const struct vk_draw_triangles *call)
959 {
960 if (call->texture)
961 vulkan_transition_texture(vk, vk->cmd, call->texture);
962
963 if (call->pipeline != vk->tracker.pipeline)
964 {
965 vkCmdBindPipeline(vk->cmd,
966 VK_PIPELINE_BIND_POINT_GRAPHICS, call->pipeline);
967 vk->tracker.pipeline = call->pipeline;
968
969 /* Changing pipeline invalidates dynamic state. */
970 vk->tracker.dirty |= VULKAN_DIRTY_DYNAMIC_BIT;
971 }
972
973 if (vk->tracker.dirty & VULKAN_DIRTY_DYNAMIC_BIT)
974 vulkan_check_dynamic_state(vk);
975
976 /* Upload descriptors */
977 {
978 VkDescriptorSet set;
979 /* Upload UBO */
980 struct vk_buffer_range range;
981 float *mvp_data_ptr = NULL;
982
983 if (!vulkan_buffer_chain_alloc(vk->context, &vk->chain->ubo,
984 call->uniform_size, &range))
985 return;
986
987 memcpy(range.data, call->uniform, call->uniform_size);
988
989 set = vulkan_descriptor_manager_alloc(
990 vk->context->device,
991 &vk->chain->descriptor_manager);
992
993 vulkan_write_quad_descriptors(
994 vk->context->device,
995 set,
996 range.buffer,
997 range.offset,
998 call->uniform_size,
999 call->texture,
1000 call->sampler);
1001
1002 vkCmdBindDescriptorSets(vk->cmd,
1003 VK_PIPELINE_BIND_POINT_GRAPHICS,
1004 vk->pipelines.layout, 0,
1005 1, &set, 0, NULL);
1006
1007 vk->tracker.view = VK_NULL_HANDLE;
1008 vk->tracker.sampler = VK_NULL_HANDLE;
1009 for (
1010 mvp_data_ptr = &vk->tracker.mvp.data[0]
1011 ; mvp_data_ptr < vk->tracker.mvp.data + 16
1012 ; mvp_data_ptr++)
1013 *mvp_data_ptr = 0.0f;
1014 }
1015
1016 /* VBO is already uploaded. */
1017 vkCmdBindVertexBuffers(vk->cmd, 0, 1,
1018 &call->vbo->buffer, &call->vbo->offset);
1019
1020 /* Draw the quad */
1021 vkCmdDraw(vk->cmd, call->vertices, 1, 0, 0);
1022 }
1023
vulkan_draw_quad(vk_t * vk,const struct vk_draw_quad * quad)1024 void vulkan_draw_quad(vk_t *vk, const struct vk_draw_quad *quad)
1025 {
1026 vulkan_transition_texture(vk, vk->cmd, quad->texture);
1027
1028 if (quad->pipeline != vk->tracker.pipeline)
1029 {
1030 vkCmdBindPipeline(vk->cmd,
1031 VK_PIPELINE_BIND_POINT_GRAPHICS, quad->pipeline);
1032
1033 vk->tracker.pipeline = quad->pipeline;
1034 /* Changing pipeline invalidates dynamic state. */
1035 vk->tracker.dirty |= VULKAN_DIRTY_DYNAMIC_BIT;
1036 }
1037
1038 if (vk->tracker.dirty & VULKAN_DIRTY_DYNAMIC_BIT)
1039 vulkan_check_dynamic_state(vk);
1040
1041 /* Upload descriptors */
1042 {
1043 VkDescriptorSet set;
1044 struct vk_buffer_range range;
1045
1046 if (!vulkan_buffer_chain_alloc(vk->context, &vk->chain->ubo,
1047 sizeof(*quad->mvp), &range))
1048 return;
1049
1050 if (
1051 string_is_equal_fast(quad->mvp,
1052 &vk->tracker.mvp, sizeof(*quad->mvp))
1053 || quad->texture->view != vk->tracker.view
1054 || quad->sampler != vk->tracker.sampler)
1055 {
1056 /* Upload UBO */
1057 struct vk_buffer_range range;
1058
1059 if (!vulkan_buffer_chain_alloc(vk->context, &vk->chain->ubo,
1060 sizeof(*quad->mvp), &range))
1061 return;
1062
1063 memcpy(range.data, quad->mvp, sizeof(*quad->mvp));
1064
1065 set = vulkan_descriptor_manager_alloc(
1066 vk->context->device,
1067 &vk->chain->descriptor_manager);
1068
1069 vulkan_write_quad_descriptors(
1070 vk->context->device,
1071 set,
1072 range.buffer,
1073 range.offset,
1074 sizeof(*quad->mvp),
1075 quad->texture,
1076 quad->sampler);
1077
1078 vkCmdBindDescriptorSets(vk->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
1079 vk->pipelines.layout, 0,
1080 1, &set, 0, NULL);
1081
1082 vk->tracker.view = quad->texture->view;
1083 vk->tracker.sampler = quad->sampler;
1084 vk->tracker.mvp = *quad->mvp;
1085 }
1086 }
1087
1088 /* Upload VBO */
1089 {
1090 struct vk_buffer_range range;
1091 if (!vulkan_buffer_chain_alloc(vk->context, &vk->chain->vbo,
1092 6 * sizeof(struct vk_vertex), &range))
1093 return;
1094
1095 {
1096 struct vk_vertex *pv = (struct vk_vertex*)range.data;
1097 const struct vk_color *color = &quad->color;
1098
1099 VULKAN_WRITE_QUAD_VBO(pv, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, color);
1100 }
1101
1102 vkCmdBindVertexBuffers(vk->cmd, 0, 1,
1103 &range.buffer, &range.offset);
1104 }
1105
1106 /* Draw the quad */
1107 vkCmdDraw(vk->cmd, 6, 1, 0, 0);
1108 }
1109
vulkan_create_buffer(const struct vulkan_context * context,size_t size,VkBufferUsageFlags usage)1110 struct vk_buffer vulkan_create_buffer(
1111 const struct vulkan_context *context,
1112 size_t size, VkBufferUsageFlags usage)
1113 {
1114 struct vk_buffer buffer;
1115 VkMemoryRequirements mem_reqs;
1116 VkBufferCreateInfo info;
1117 VkMemoryAllocateInfo alloc;
1118
1119 info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
1120 info.pNext = NULL;
1121 info.flags = 0;
1122 info.size = size;
1123 info.usage = usage;
1124 info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1125 info.queueFamilyIndexCount = 0;
1126 info.pQueueFamilyIndices = NULL;
1127 vkCreateBuffer(context->device, &info, NULL, &buffer.buffer);
1128
1129 vkGetBufferMemoryRequirements(context->device, buffer.buffer, &mem_reqs);
1130
1131 alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1132 alloc.pNext = NULL;
1133 alloc.allocationSize = mem_reqs.size;
1134 alloc.memoryTypeIndex = vulkan_find_memory_type(
1135 &context->memory_properties,
1136 mem_reqs.memoryTypeBits,
1137 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
1138 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
1139 vkAllocateMemory(context->device, &alloc, NULL, &buffer.memory);
1140 vkBindBufferMemory(context->device, buffer.buffer, buffer.memory, 0);
1141
1142 buffer.size = size;
1143
1144 vkMapMemory(context->device,
1145 buffer.memory, 0, buffer.size, 0, &buffer.mapped);
1146 return buffer;
1147 }
1148
vulkan_destroy_buffer(VkDevice device,struct vk_buffer * buffer)1149 void vulkan_destroy_buffer(
1150 VkDevice device,
1151 struct vk_buffer *buffer)
1152 {
1153 vkUnmapMemory(device, buffer->memory);
1154 vkFreeMemory(device, buffer->memory, NULL);
1155
1156 vkDestroyBuffer(device, buffer->buffer, NULL);
1157
1158 memset(buffer, 0, sizeof(*buffer));
1159 }
1160
vulkan_alloc_descriptor_pool(VkDevice device,const struct vk_descriptor_manager * manager)1161 static struct vk_descriptor_pool *vulkan_alloc_descriptor_pool(
1162 VkDevice device,
1163 const struct vk_descriptor_manager *manager)
1164 {
1165 unsigned i;
1166 VkDescriptorPoolCreateInfo pool_info;
1167 VkDescriptorSetAllocateInfo alloc_info;
1168 struct vk_descriptor_pool *pool =
1169 (struct vk_descriptor_pool*)malloc(sizeof(*pool));
1170 if (!pool)
1171 return NULL;
1172
1173 pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
1174 pool_info.pNext = NULL;
1175 pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
1176 pool_info.maxSets = VULKAN_DESCRIPTOR_MANAGER_BLOCK_SETS;
1177 pool_info.poolSizeCount = manager->num_sizes;
1178 pool_info.pPoolSizes = manager->sizes;
1179
1180 pool->pool = VK_NULL_HANDLE;
1181 for (i = 0; i < VULKAN_DESCRIPTOR_MANAGER_BLOCK_SETS; i++)
1182 pool->sets[i] = VK_NULL_HANDLE;
1183 pool->next = NULL;
1184
1185 vkCreateDescriptorPool(device, &pool_info, NULL, &pool->pool);
1186
1187 /* Just allocate all descriptor sets up front. */
1188 alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
1189 alloc_info.pNext = NULL;
1190 alloc_info.descriptorPool = pool->pool;
1191 alloc_info.descriptorSetCount = 1;
1192 alloc_info.pSetLayouts = &manager->set_layout;
1193
1194 for (i = 0; i < VULKAN_DESCRIPTOR_MANAGER_BLOCK_SETS; i++)
1195 vkAllocateDescriptorSets(device, &alloc_info, &pool->sets[i]);
1196
1197 return pool;
1198 }
1199
vulkan_descriptor_manager_alloc(VkDevice device,struct vk_descriptor_manager * manager)1200 VkDescriptorSet vulkan_descriptor_manager_alloc(
1201 VkDevice device, struct vk_descriptor_manager *manager)
1202 {
1203 if (manager->count < VULKAN_DESCRIPTOR_MANAGER_BLOCK_SETS)
1204 return manager->current->sets[manager->count++];
1205
1206 while (manager->current->next)
1207 {
1208 manager->current = manager->current->next;
1209 manager->count = 0;
1210 return manager->current->sets[manager->count++];
1211 }
1212
1213 manager->current->next = vulkan_alloc_descriptor_pool(device, manager);
1214 retro_assert(manager->current->next);
1215
1216 manager->current = manager->current->next;
1217 manager->count = 0;
1218 return manager->current->sets[manager->count++];
1219 }
1220
vulkan_create_descriptor_manager(VkDevice device,const VkDescriptorPoolSize * sizes,unsigned num_sizes,VkDescriptorSetLayout set_layout)1221 struct vk_descriptor_manager vulkan_create_descriptor_manager(
1222 VkDevice device,
1223 const VkDescriptorPoolSize *sizes,
1224 unsigned num_sizes,
1225 VkDescriptorSetLayout set_layout)
1226 {
1227 unsigned i;
1228 struct vk_descriptor_manager manager;
1229
1230 retro_assert(num_sizes <= VULKAN_MAX_DESCRIPTOR_POOL_SIZES);
1231
1232 manager.current = NULL;
1233 manager.count = 0;
1234
1235 for (i = 0; i < VULKAN_MAX_DESCRIPTOR_POOL_SIZES; i++)
1236 {
1237 manager.sizes[i].type = VK_DESCRIPTOR_TYPE_SAMPLER;
1238 manager.sizes[i].descriptorCount = 0;
1239 }
1240 memcpy(manager.sizes, sizes, num_sizes * sizeof(*sizes));
1241 manager.set_layout = set_layout;
1242 manager.num_sizes = num_sizes;
1243
1244 manager.head = vulkan_alloc_descriptor_pool(device, &manager);
1245 retro_assert(manager.head);
1246 return manager;
1247 }
1248
vulkan_destroy_descriptor_manager(VkDevice device,struct vk_descriptor_manager * manager)1249 void vulkan_destroy_descriptor_manager(
1250 VkDevice device,
1251 struct vk_descriptor_manager *manager)
1252 {
1253 struct vk_descriptor_pool *node = manager->head;
1254
1255 while (node)
1256 {
1257 struct vk_descriptor_pool *next = node->next;
1258
1259 vkFreeDescriptorSets(device, node->pool,
1260 VULKAN_DESCRIPTOR_MANAGER_BLOCK_SETS, node->sets);
1261 vkDestroyDescriptorPool(device, node->pool, NULL);
1262
1263 free(node);
1264 node = next;
1265 }
1266
1267 memset(manager, 0, sizeof(*manager));
1268 }
1269
vulkan_buffer_chain_step(struct vk_buffer_chain * chain)1270 static void vulkan_buffer_chain_step(struct vk_buffer_chain *chain)
1271 {
1272 chain->current = chain->current->next;
1273 chain->offset = 0;
1274 }
1275
vulkan_buffer_chain_suballoc(struct vk_buffer_chain * chain,size_t size,struct vk_buffer_range * range)1276 static bool vulkan_buffer_chain_suballoc(struct vk_buffer_chain *chain,
1277 size_t size, struct vk_buffer_range *range)
1278 {
1279 VkDeviceSize next_offset = chain->offset + size;
1280 if (next_offset <= chain->current->buffer.size)
1281 {
1282 range->data = (uint8_t*)chain->current->buffer.mapped + chain->offset;
1283 range->buffer = chain->current->buffer.buffer;
1284 range->offset = chain->offset;
1285 chain->offset = (next_offset + chain->alignment - 1)
1286 & ~(chain->alignment - 1);
1287
1288 return true;
1289 }
1290
1291 return false;
1292 }
1293
vulkan_buffer_chain_alloc_node(const struct vulkan_context * context,size_t size,VkBufferUsageFlags usage)1294 static struct vk_buffer_node *vulkan_buffer_chain_alloc_node(
1295 const struct vulkan_context *context,
1296 size_t size, VkBufferUsageFlags usage)
1297 {
1298 struct vk_buffer_node *node = (struct vk_buffer_node*)
1299 malloc(sizeof(*node));
1300 if (!node)
1301 return NULL;
1302
1303 node->buffer = vulkan_create_buffer(
1304 context, size, usage);
1305 node->next = NULL;
1306 return node;
1307 }
1308
vulkan_buffer_chain_init(VkDeviceSize block_size,VkDeviceSize alignment,VkBufferUsageFlags usage)1309 struct vk_buffer_chain vulkan_buffer_chain_init(
1310 VkDeviceSize block_size,
1311 VkDeviceSize alignment,
1312 VkBufferUsageFlags usage)
1313 {
1314 struct vk_buffer_chain chain;
1315
1316 chain.block_size = block_size;
1317 chain.alignment = alignment;
1318 chain.offset = 0;
1319 chain.usage = usage;
1320 chain.head = NULL;
1321 chain.current = NULL;
1322
1323 return chain;
1324 }
1325
vulkan_buffer_chain_alloc(const struct vulkan_context * context,struct vk_buffer_chain * chain,size_t size,struct vk_buffer_range * range)1326 bool vulkan_buffer_chain_alloc(const struct vulkan_context *context,
1327 struct vk_buffer_chain *chain,
1328 size_t size, struct vk_buffer_range *range)
1329 {
1330 if (!chain->head)
1331 {
1332 chain->head = vulkan_buffer_chain_alloc_node(context,
1333 chain->block_size, chain->usage);
1334 if (!chain->head)
1335 return false;
1336
1337 chain->current = chain->head;
1338 chain->offset = 0;
1339 }
1340
1341 if (vulkan_buffer_chain_suballoc(chain, size, range))
1342 return true;
1343
1344 /* We've exhausted the current chain, traverse list until we
1345 * can find a block we can use. Usually, we just step once. */
1346 while (chain->current->next)
1347 {
1348 vulkan_buffer_chain_step(chain);
1349 if (vulkan_buffer_chain_suballoc(chain, size, range))
1350 return true;
1351 }
1352
1353 /* We have to allocate a new node, might allocate larger
1354 * buffer here than block_size in case we have
1355 * a very large allocation. */
1356 if (size < chain->block_size)
1357 size = chain->block_size;
1358
1359 chain->current->next = vulkan_buffer_chain_alloc_node(
1360 context, size, chain->usage);
1361 if (!chain->current->next)
1362 return false;
1363
1364 vulkan_buffer_chain_step(chain);
1365 /* This cannot possibly fail. */
1366 retro_assert(vulkan_buffer_chain_suballoc(chain, size, range));
1367 return true;
1368 }
1369
vulkan_buffer_chain_free(VkDevice device,struct vk_buffer_chain * chain)1370 void vulkan_buffer_chain_free(
1371 VkDevice device,
1372 struct vk_buffer_chain *chain)
1373 {
1374 struct vk_buffer_node *node = chain->head;
1375 while (node)
1376 {
1377 struct vk_buffer_node *next = node->next;
1378 vulkan_destroy_buffer(device, &node->buffer);
1379
1380 free(node);
1381 node = next;
1382 }
1383 memset(chain, 0, sizeof(*chain));
1384 }
1385
vulkan_load_instance_symbols(gfx_ctx_vulkan_data_t * vk)1386 static bool vulkan_load_instance_symbols(gfx_ctx_vulkan_data_t *vk)
1387 {
1388 if (!vulkan_symbol_wrapper_load_core_instance_symbols(vk->context.instance))
1389 return false;
1390
1391 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance, vkDestroySurfaceKHR);
1392 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance, vkGetPhysicalDeviceSurfaceSupportKHR);
1393 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance, vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
1394 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance, vkGetPhysicalDeviceSurfaceFormatsKHR);
1395 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance, vkGetPhysicalDeviceSurfacePresentModesKHR);
1396 return true;
1397 }
1398
vulkan_load_device_symbols(gfx_ctx_vulkan_data_t * vk)1399 static bool vulkan_load_device_symbols(gfx_ctx_vulkan_data_t *vk)
1400 {
1401 if (!vulkan_symbol_wrapper_load_core_device_symbols(vk->context.device))
1402 return false;
1403
1404 VULKAN_SYMBOL_WRAPPER_LOAD_DEVICE_EXTENSION_SYMBOL(vk->context.device, vkCreateSwapchainKHR);
1405 VULKAN_SYMBOL_WRAPPER_LOAD_DEVICE_EXTENSION_SYMBOL(vk->context.device, vkDestroySwapchainKHR);
1406 VULKAN_SYMBOL_WRAPPER_LOAD_DEVICE_EXTENSION_SYMBOL(vk->context.device, vkGetSwapchainImagesKHR);
1407 VULKAN_SYMBOL_WRAPPER_LOAD_DEVICE_EXTENSION_SYMBOL(vk->context.device, vkAcquireNextImageKHR);
1408 VULKAN_SYMBOL_WRAPPER_LOAD_DEVICE_EXTENSION_SYMBOL(vk->context.device, vkQueuePresentKHR);
1409 return true;
1410 }
1411
vulkan_find_extensions(const char ** exts,unsigned num_exts,const VkExtensionProperties * properties,unsigned property_count)1412 static bool vulkan_find_extensions(const char **exts, unsigned num_exts,
1413 const VkExtensionProperties *properties, unsigned property_count)
1414 {
1415 unsigned i, ext;
1416 bool found;
1417 for (ext = 0; ext < num_exts; ext++)
1418 {
1419 found = false;
1420 for (i = 0; i < property_count; i++)
1421 {
1422 if (string_is_equal(exts[ext], properties[i].extensionName))
1423 {
1424 found = true;
1425 break;
1426 }
1427 }
1428
1429 if (!found)
1430 return false;
1431 }
1432 return true;
1433 }
1434
vulkan_find_instance_extensions(const char ** exts,unsigned num_exts)1435 static bool vulkan_find_instance_extensions(const char **exts, unsigned num_exts)
1436 {
1437 uint32_t property_count;
1438 bool ret = true;
1439 VkExtensionProperties *properties = NULL;
1440
1441 if (vkEnumerateInstanceExtensionProperties(NULL, &property_count, NULL) != VK_SUCCESS)
1442 return false;
1443
1444 properties = (VkExtensionProperties*)malloc(property_count * sizeof(*properties));
1445 if (!properties)
1446 {
1447 ret = false;
1448 goto end;
1449 }
1450
1451 if (vkEnumerateInstanceExtensionProperties(NULL, &property_count, properties) != VK_SUCCESS)
1452 {
1453 ret = false;
1454 goto end;
1455 }
1456
1457 if (!vulkan_find_extensions(exts, num_exts, properties, property_count))
1458 {
1459 RARCH_ERR("[Vulkan]: Could not find instance extensions. Will attempt without them.\n");
1460 ret = false;
1461 goto end;
1462 }
1463
1464 end:
1465 free(properties);
1466 return ret;
1467 }
1468
vulkan_find_device_extensions(VkPhysicalDevice gpu,const char ** enabled,unsigned * enabled_count,const char ** exts,unsigned num_exts,const char ** optional_exts,unsigned num_optional_exts)1469 static bool vulkan_find_device_extensions(VkPhysicalDevice gpu,
1470 const char **enabled, unsigned *enabled_count,
1471 const char **exts, unsigned num_exts,
1472 const char **optional_exts, unsigned num_optional_exts)
1473 {
1474 uint32_t property_count;
1475 unsigned i;
1476 bool ret = true;
1477 VkExtensionProperties *properties = NULL;
1478
1479 if (vkEnumerateDeviceExtensionProperties(gpu, NULL, &property_count, NULL) != VK_SUCCESS)
1480 return false;
1481
1482 properties = (VkExtensionProperties*)malloc(property_count * sizeof(*properties));
1483 if (!properties)
1484 {
1485 ret = false;
1486 goto end;
1487 }
1488
1489 if (vkEnumerateDeviceExtensionProperties(gpu, NULL, &property_count, properties) != VK_SUCCESS)
1490 {
1491 ret = false;
1492 goto end;
1493 }
1494
1495 if (!vulkan_find_extensions(exts, num_exts, properties, property_count))
1496 {
1497 RARCH_ERR("[Vulkan]: Could not find device extension. Will attempt without it.\n");
1498 ret = false;
1499 goto end;
1500 }
1501
1502 memcpy((void*)enabled, exts, num_exts * sizeof(*exts));
1503 *enabled_count = num_exts;
1504
1505 for (i = 0; i < num_optional_exts; i++)
1506 if (vulkan_find_extensions(&optional_exts[i], 1, properties, property_count))
1507 enabled[(*enabled_count)++] = optional_exts[i];
1508
1509 end:
1510 free(properties);
1511 return ret;
1512 }
1513
vulkan_context_init_gpu(gfx_ctx_vulkan_data_t * vk)1514 static bool vulkan_context_init_gpu(gfx_ctx_vulkan_data_t *vk)
1515 {
1516 unsigned i;
1517 uint32_t gpu_count = 0;
1518 VkPhysicalDevice *gpus = NULL;
1519 union string_list_elem_attr attr = {0};
1520 settings_t *settings = config_get_ptr();
1521 int gpu_index = settings->ints.vulkan_gpu_index;
1522
1523 if (vkEnumeratePhysicalDevices(vk->context.instance,
1524 &gpu_count, NULL) != VK_SUCCESS)
1525 {
1526 RARCH_ERR("[Vulkan]: Failed to enumerate physical devices.\n");
1527 return false;
1528 }
1529
1530 gpus = (VkPhysicalDevice*)calloc(gpu_count, sizeof(*gpus));
1531 if (!gpus)
1532 {
1533 RARCH_ERR("[Vulkan]: Failed to enumerate physical devices.\n");
1534 return false;
1535 }
1536
1537 if (vkEnumeratePhysicalDevices(vk->context.instance,
1538 &gpu_count, gpus) != VK_SUCCESS)
1539 {
1540 RARCH_ERR("[Vulkan]: Failed to enumerate physical devices.\n");
1541 free(gpus);
1542 return false;
1543 }
1544
1545 if (gpu_count < 1)
1546 {
1547 RARCH_ERR("[Vulkan]: Failed to enumerate Vulkan physical device.\n");
1548 free(gpus);
1549 return false;
1550 }
1551
1552 if (vk->gpu_list)
1553 string_list_free(vk->gpu_list);
1554
1555 vk->gpu_list = string_list_new();
1556
1557 for (i = 0; i < gpu_count; i++)
1558 {
1559 VkPhysicalDeviceProperties gpu_properties;
1560
1561 vkGetPhysicalDeviceProperties(gpus[i],
1562 &gpu_properties);
1563
1564 RARCH_LOG("[Vulkan]: Found GPU at index %d: %s\n", i, gpu_properties.deviceName);
1565
1566 string_list_append(vk->gpu_list, gpu_properties.deviceName, attr);
1567 }
1568
1569 video_driver_set_gpu_api_devices(GFX_CTX_VULKAN_API, vk->gpu_list);
1570
1571 if (0 <= gpu_index && gpu_index < (int)gpu_count)
1572 {
1573 RARCH_LOG("[Vulkan]: Using GPU index %d.\n", gpu_index);
1574 vk->context.gpu = gpus[gpu_index];
1575 }
1576 else
1577 {
1578 RARCH_WARN("[Vulkan]: Invalid GPU index %d, using first device found.\n", gpu_index);
1579 vk->context.gpu = gpus[0];
1580 }
1581
1582 free(gpus);
1583 return true;
1584 }
1585
vulkan_context_init_device(gfx_ctx_vulkan_data_t * vk)1586 static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
1587 {
1588 bool use_device_ext;
1589 uint32_t queue_count;
1590 unsigned i;
1591 static const float one = 1.0f;
1592 bool found_queue = false;
1593
1594 VkPhysicalDeviceFeatures features = { false };
1595 VkDeviceQueueCreateInfo queue_info = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
1596 VkDeviceCreateInfo device_info = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
1597
1598 const char *enabled_device_extensions[8];
1599 unsigned enabled_device_extension_count = 0;
1600
1601 static const char *device_extensions[] = {
1602 "VK_KHR_swapchain",
1603 };
1604
1605 static const char *optional_device_extensions[] = {
1606 "VK_KHR_sampler_mirror_clamp_to_edge",
1607 };
1608
1609 struct retro_hw_render_context_negotiation_interface_vulkan *iface =
1610 (struct retro_hw_render_context_negotiation_interface_vulkan*)video_driver_get_context_negotiation_interface();
1611
1612 if (iface && iface->interface_type != RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN)
1613 {
1614 RARCH_WARN("[Vulkan]: Got HW context negotiation interface, but it's the wrong API.\n");
1615 iface = NULL;
1616 }
1617
1618 if (iface && iface->interface_version != RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION)
1619 {
1620 RARCH_WARN("[Vulkan]: Got HW context negotiation interface, but it's the wrong interface version.\n");
1621 iface = NULL;
1622 }
1623
1624 if (!cached_device_vk && iface && iface->create_device)
1625 {
1626 struct retro_vulkan_context context = { 0 };
1627 const VkPhysicalDeviceFeatures features = { 0 };
1628
1629 bool ret = iface->create_device(&context, vk->context.instance,
1630 vk->context.gpu,
1631 vk->vk_surface,
1632 vulkan_symbol_wrapper_instance_proc_addr(),
1633 device_extensions,
1634 ARRAY_SIZE(device_extensions),
1635 NULL,
1636 0,
1637 &features);
1638
1639 if (!ret)
1640 {
1641 RARCH_WARN("[Vulkan]: Failed to create device with negotiation interface. Falling back to default path.\n");
1642 }
1643 else
1644 {
1645 vk->context.destroy_device = iface->destroy_device;
1646
1647 vk->context.device = context.device;
1648 vk->context.queue = context.queue;
1649 vk->context.gpu = context.gpu;
1650 vk->context.graphics_queue_index = context.queue_family_index;
1651
1652 if (context.presentation_queue != context.queue)
1653 {
1654 RARCH_ERR("[Vulkan]: Present queue != graphics queue. This is currently not supported.\n");
1655 return false;
1656 }
1657 }
1658 }
1659
1660 if (cached_device_vk && cached_destroy_device_vk)
1661 {
1662 vk->context.destroy_device = cached_destroy_device_vk;
1663 cached_destroy_device_vk = NULL;
1664 }
1665
1666 if (!vulkan_context_init_gpu(vk))
1667 return false;
1668
1669 vkGetPhysicalDeviceProperties(vk->context.gpu,
1670 &vk->context.gpu_properties);
1671 vkGetPhysicalDeviceMemoryProperties(vk->context.gpu,
1672 &vk->context.memory_properties);
1673
1674 #ifdef VULKAN_EMULATE_MAILBOX
1675 /* Win32 windowed mode seems to deal just fine with toggling VSync.
1676 * Fullscreen however ... */
1677 vk->emulate_mailbox = vk->fullscreen;
1678 #endif
1679
1680 /* If we're emulating mailbox, stick to using fences rather than semaphores.
1681 * Avoids some really weird driver bugs. */
1682 if (!vk->emulate_mailbox)
1683 {
1684 if (vk->context.gpu_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
1685 {
1686 vk->use_wsi_semaphore = true;
1687 RARCH_LOG("[Vulkan]: Using semaphores for WSI acquire.\n");
1688 }
1689 else
1690 {
1691 vk->use_wsi_semaphore = false;
1692 RARCH_LOG("[Vulkan]: Using fences for WSI acquire.\n");
1693 }
1694 }
1695
1696 RARCH_LOG("[Vulkan]: Using GPU: %s\n", vk->context.gpu_properties.deviceName);
1697
1698 {
1699 char device_str[128];
1700 char driver_version[64];
1701 char api_version[64];
1702 char version_str[128];
1703 int pos = 0;
1704
1705 device_str[0] = driver_version[0] = api_version[0] = version_str[0] = '\0';
1706
1707 strlcpy(device_str, vk->context.gpu_properties.deviceName, sizeof(device_str));
1708 strlcat(device_str, " ", sizeof(device_str));
1709
1710 pos += snprintf(driver_version + pos, sizeof(driver_version) - pos, "%u", VK_VERSION_MAJOR(vk->context.gpu_properties.driverVersion));
1711 strlcat(driver_version, ".", sizeof(driver_version));
1712 pos++;
1713 pos += snprintf(driver_version + pos, sizeof(driver_version) - pos, "%u", VK_VERSION_MINOR(vk->context.gpu_properties.driverVersion));
1714 pos++;
1715 strlcat(driver_version, ".", sizeof(driver_version));
1716 pos += snprintf(driver_version + pos, sizeof(driver_version) - pos, "%u", VK_VERSION_PATCH(vk->context.gpu_properties.driverVersion));
1717
1718 strlcat(device_str, driver_version, sizeof(device_str));
1719
1720 pos = 0;
1721
1722 pos += snprintf(api_version + pos, sizeof(api_version) - pos, "%u", VK_VERSION_MAJOR(vk->context.gpu_properties.apiVersion));
1723 strlcat(api_version, ".", sizeof(api_version));
1724 pos++;
1725 pos += snprintf(api_version + pos, sizeof(api_version) - pos, "%u", VK_VERSION_MINOR(vk->context.gpu_properties.apiVersion));
1726 pos++;
1727 strlcat(api_version, ".", sizeof(api_version));
1728 pos += snprintf(api_version + pos, sizeof(api_version) - pos, "%u", VK_VERSION_PATCH(vk->context.gpu_properties.apiVersion));
1729
1730 strlcat(version_str, api_version, sizeof(device_str));
1731
1732 video_driver_set_gpu_device_string(device_str);
1733 video_driver_set_gpu_api_version_string(version_str);
1734 }
1735
1736 if (vk->context.device == VK_NULL_HANDLE)
1737 {
1738 VkQueueFamilyProperties *queue_properties = NULL;
1739 vkGetPhysicalDeviceQueueFamilyProperties(vk->context.gpu,
1740 &queue_count, NULL);
1741
1742 if (queue_count < 1)
1743 {
1744 RARCH_ERR("[Vulkan]: Invalid number of queues detected.\n");
1745 return false;
1746 }
1747
1748 queue_properties = (VkQueueFamilyProperties*)malloc(queue_count * sizeof(*queue_properties));
1749 if (!queue_properties)
1750 return false;
1751
1752 vkGetPhysicalDeviceQueueFamilyProperties(vk->context.gpu,
1753 &queue_count, queue_properties);
1754
1755 for (i = 0; i < queue_count; i++)
1756 {
1757 VkQueueFlags required;
1758 VkBool32 supported = VK_FALSE;
1759 vkGetPhysicalDeviceSurfaceSupportKHR(
1760 vk->context.gpu, i,
1761 vk->vk_surface, &supported);
1762
1763 required = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
1764 if (supported && ((queue_properties[i].queueFlags & required) == required))
1765 {
1766 vk->context.graphics_queue_index = i;
1767 RARCH_LOG("[Vulkan]: Queue family %u supports %u sub-queues.\n",
1768 i, queue_properties[i].queueCount);
1769 found_queue = true;
1770 break;
1771 }
1772 }
1773
1774 free(queue_properties);
1775
1776 if (!found_queue)
1777 {
1778 RARCH_ERR("[Vulkan]: Did not find suitable graphics queue.\n");
1779 return false;
1780 }
1781
1782 use_device_ext = vulkan_find_device_extensions(vk->context.gpu,
1783 enabled_device_extensions, &enabled_device_extension_count,
1784 device_extensions, ARRAY_SIZE(device_extensions),
1785 optional_device_extensions, ARRAY_SIZE(optional_device_extensions));
1786
1787 if (!use_device_ext)
1788 {
1789 RARCH_ERR("[Vulkan]: Could not find required device extensions.\n");
1790 return false;
1791 }
1792
1793 queue_info.queueFamilyIndex = vk->context.graphics_queue_index;
1794 queue_info.queueCount = 1;
1795 queue_info.pQueuePriorities = &one;
1796
1797 device_info.queueCreateInfoCount = 1;
1798 device_info.pQueueCreateInfos = &queue_info;
1799 device_info.enabledExtensionCount = enabled_device_extension_count;
1800 device_info.ppEnabledExtensionNames = enabled_device_extension_count ? enabled_device_extensions : NULL;
1801 device_info.pEnabledFeatures = &features;
1802
1803 if (cached_device_vk)
1804 {
1805 vk->context.device = cached_device_vk;
1806 cached_device_vk = NULL;
1807
1808 video_driver_set_video_cache_context_ack();
1809 RARCH_LOG("[Vulkan]: Using cached Vulkan context.\n");
1810 }
1811 else if (vkCreateDevice(vk->context.gpu, &device_info,
1812 NULL, &vk->context.device) != VK_SUCCESS)
1813 {
1814 RARCH_ERR("[Vulkan]: Failed to create device.\n");
1815 return false;
1816 }
1817 }
1818
1819 if (!vulkan_load_device_symbols(vk))
1820 {
1821 RARCH_ERR("[Vulkan]: Failed to load device symbols.\n");
1822 return false;
1823 }
1824
1825 vkGetDeviceQueue(vk->context.device,
1826 vk->context.graphics_queue_index, 0, &vk->context.queue);
1827
1828 #ifdef HAVE_THREADS
1829 vk->context.queue_lock = slock_new();
1830 if (!vk->context.queue_lock)
1831 {
1832 RARCH_ERR("[Vulkan]: Failed to create queue lock.\n");
1833 return false;
1834 }
1835 #endif
1836
1837 return true;
1838 }
1839
vulkan_context_init(gfx_ctx_vulkan_data_t * vk,enum vulkan_wsi_type type)1840 bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
1841 enum vulkan_wsi_type type)
1842 {
1843 unsigned i;
1844 VkResult res;
1845 PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
1846 VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
1847 VkApplicationInfo app = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
1848
1849 const char *instance_extensions[4];
1850 unsigned ext_count = 0;
1851
1852 #ifdef VULKAN_DEBUG
1853 instance_extensions[ext_count++] = "VK_EXT_debug_report";
1854 static const char *instance_layers[] = { "VK_LAYER_KHRONOS_validation" };
1855 #endif
1856
1857 bool use_instance_ext;
1858 struct retro_hw_render_context_negotiation_interface_vulkan *iface =
1859 (struct retro_hw_render_context_negotiation_interface_vulkan*)video_driver_get_context_negotiation_interface();
1860
1861 if (iface && iface->interface_type != RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN)
1862 {
1863 RARCH_WARN("[Vulkan]: Got HW context negotiation interface, but it's the wrong API.\n");
1864 iface = NULL;
1865 }
1866
1867 if (iface && iface->interface_version != RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION)
1868 {
1869 RARCH_WARN("[Vulkan]: Got HW context negotiation interface, but it's the wrong interface version.\n");
1870 iface = NULL;
1871 }
1872
1873 instance_extensions[ext_count++] = "VK_KHR_surface";
1874
1875 switch (type)
1876 {
1877 case VULKAN_WSI_WAYLAND:
1878 instance_extensions[ext_count++] = "VK_KHR_wayland_surface";
1879 break;
1880 case VULKAN_WSI_ANDROID:
1881 instance_extensions[ext_count++] = "VK_KHR_android_surface";
1882 break;
1883 case VULKAN_WSI_WIN32:
1884 instance_extensions[ext_count++] = "VK_KHR_win32_surface";
1885 break;
1886 case VULKAN_WSI_XLIB:
1887 instance_extensions[ext_count++] = "VK_KHR_xlib_surface";
1888 break;
1889 case VULKAN_WSI_XCB:
1890 instance_extensions[ext_count++] = "VK_KHR_xcb_surface";
1891 break;
1892 case VULKAN_WSI_MIR:
1893 instance_extensions[ext_count++] = "VK_KHR_mir_surface";
1894 break;
1895 case VULKAN_WSI_DISPLAY:
1896 instance_extensions[ext_count++] = "VK_KHR_display";
1897 break;
1898 case VULKAN_WSI_MVK_MACOS:
1899 instance_extensions[ext_count++] = "VK_MVK_macos_surface";
1900 break;
1901 case VULKAN_WSI_MVK_IOS:
1902 instance_extensions[ext_count++] = "VK_MVK_ios_surface";
1903 break;
1904 case VULKAN_WSI_NONE:
1905 default:
1906 break;
1907 }
1908
1909 if (!vulkan_library)
1910 {
1911 #ifdef _WIN32
1912 vulkan_library = dylib_load("vulkan-1.dll");
1913 #elif __APPLE__
1914 vulkan_library = dylib_load("libMoltenVK.dylib");
1915 #else
1916 vulkan_library = dylib_load("libvulkan.so");
1917 if (!vulkan_library)
1918 vulkan_library = dylib_load("libvulkan.so.1");
1919 #endif
1920 }
1921
1922 if (!vulkan_library)
1923 {
1924 RARCH_ERR("[Vulkan]: Failed to open Vulkan loader.\n");
1925 return false;
1926 }
1927
1928 RARCH_LOG("[Vulkan]: Vulkan dynamic library loaded.\n");
1929
1930 GetInstanceProcAddr =
1931 (PFN_vkGetInstanceProcAddr)dylib_proc(vulkan_library, "vkGetInstanceProcAddr");
1932
1933 if (!GetInstanceProcAddr)
1934 {
1935 RARCH_ERR("[Vulkan]: Failed to load vkGetInstanceProcAddr symbol, broken loader?\n");
1936 return false;
1937 }
1938
1939 vulkan_symbol_wrapper_init(GetInstanceProcAddr);
1940
1941 if (!vulkan_symbol_wrapper_load_global_symbols())
1942 {
1943 RARCH_ERR("[Vulkan]: Failed to load global Vulkan symbols, broken loader?\n");
1944 return false;
1945 }
1946
1947 use_instance_ext = vulkan_find_instance_extensions(instance_extensions, ext_count);
1948
1949 app.pApplicationName = msg_hash_to_str(MSG_PROGRAM);
1950 app.applicationVersion = 0;
1951 app.pEngineName = msg_hash_to_str(MSG_PROGRAM);
1952 app.engineVersion = 0;
1953 app.apiVersion = VK_MAKE_VERSION(1, 0, 18);
1954
1955 info.pApplicationInfo = &app;
1956 info.enabledExtensionCount = use_instance_ext ? ext_count : 0;
1957 info.ppEnabledExtensionNames = use_instance_ext ? instance_extensions : NULL;
1958 #ifdef VULKAN_DEBUG
1959 info.enabledLayerCount = ARRAY_SIZE(instance_layers);
1960 info.ppEnabledLayerNames = instance_layers;
1961 #endif
1962
1963 if (iface && iface->get_application_info)
1964 {
1965 info.pApplicationInfo = iface->get_application_info();
1966 if (info.pApplicationInfo->pApplicationName)
1967 {
1968 RARCH_LOG("[Vulkan]: App: %s (version %u)\n",
1969 info.pApplicationInfo->pApplicationName,
1970 info.pApplicationInfo->applicationVersion);
1971 }
1972
1973 if (info.pApplicationInfo->pEngineName)
1974 {
1975 RARCH_LOG("[Vulkan]: Engine: %s (version %u)\n",
1976 info.pApplicationInfo->pEngineName,
1977 info.pApplicationInfo->engineVersion);
1978 }
1979 }
1980
1981 if (cached_instance_vk)
1982 {
1983 vk->context.instance = cached_instance_vk;
1984 cached_instance_vk = NULL;
1985 res = VK_SUCCESS;
1986 }
1987 else
1988 res = vkCreateInstance(&info, NULL, &vk->context.instance);
1989
1990 #ifdef VULKAN_DEBUG
1991 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance,
1992 vkCreateDebugReportCallbackEXT);
1993 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance,
1994 vkDebugReportMessageEXT);
1995 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance,
1996 vkDestroyDebugReportCallbackEXT);
1997
1998 {
1999 VkDebugReportCallbackCreateInfoEXT info =
2000 { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT };
2001 info.flags =
2002 VK_DEBUG_REPORT_ERROR_BIT_EXT |
2003 VK_DEBUG_REPORT_WARNING_BIT_EXT |
2004 VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
2005 info.pfnCallback = vulkan_debug_cb;
2006
2007 if (vk->context.instance)
2008 vkCreateDebugReportCallbackEXT(vk->context.instance, &info, NULL,
2009 &vk->context.debug_callback);
2010 }
2011 RARCH_LOG("[Vulkan]: Enabling Vulkan debug layers.\n");
2012 #endif
2013
2014 /* Try different API versions if driver has compatible
2015 * but slightly different VK_API_VERSION. */
2016 for (i = 1; i < 4 && res == VK_ERROR_INCOMPATIBLE_DRIVER; i++)
2017 {
2018 info.pApplicationInfo = &app;
2019 app.apiVersion = VK_MAKE_VERSION(1, 0, i);
2020 res = vkCreateInstance(&info, NULL, &vk->context.instance);
2021 }
2022
2023 if (res != VK_SUCCESS)
2024 {
2025 RARCH_ERR("Failed to create Vulkan instance (%d).\n", res);
2026 return false;
2027 }
2028
2029 if (!vulkan_load_instance_symbols(vk))
2030 {
2031 RARCH_ERR("[Vulkan]: Failed to load instance symbols.\n");
2032 return false;
2033 }
2034
2035 return true;
2036 }
2037
vulkan_update_display_mode(unsigned * width,unsigned * height,const VkDisplayModePropertiesKHR * mode,const struct vulkan_display_surface_info * info)2038 static bool vulkan_update_display_mode(
2039 unsigned *width,
2040 unsigned *height,
2041 const VkDisplayModePropertiesKHR *mode,
2042 const struct vulkan_display_surface_info *info)
2043 {
2044 unsigned visible_width = mode->parameters.visibleRegion.width;
2045 unsigned visible_height = mode->parameters.visibleRegion.height;
2046
2047 if (!info->width || !info->height)
2048 {
2049 /* Strategy here is to pick something which is largest resolution. */
2050 unsigned area = visible_width * visible_height;
2051 if (area > (*width) * (*height))
2052 {
2053 *width = visible_width;
2054 *height = visible_height;
2055 return true;
2056 }
2057 }
2058 else
2059 {
2060 /* For particular resolutions, find the closest. */
2061 int delta_x = (int)info->width - (int)visible_width;
2062 int delta_y = (int)info->height - (int)visible_height;
2063 int old_delta_x = (int)info->width - (int)*width;
2064 int old_delta_y = (int)info->height - (int)*height;
2065
2066 int dist = delta_x * delta_x + delta_y * delta_y;
2067 int old_dist = old_delta_x * old_delta_x + old_delta_y * old_delta_y;
2068
2069 if (dist < old_dist)
2070 {
2071 *width = visible_width;
2072 *height = visible_height;
2073 return true;
2074 }
2075 }
2076
2077 return false;
2078 }
2079
vulkan_create_display_surface(gfx_ctx_vulkan_data_t * vk,unsigned * width,unsigned * height,const struct vulkan_display_surface_info * info)2080 static bool vulkan_create_display_surface(gfx_ctx_vulkan_data_t *vk,
2081 unsigned *width, unsigned *height,
2082 const struct vulkan_display_surface_info *info)
2083 {
2084 bool ret = true;
2085 uint32_t display_count = 0;
2086 uint32_t plane_count = 0;
2087 VkDisplayPropertiesKHR *displays = NULL;
2088 VkDisplayPlanePropertiesKHR *planes = NULL;
2089 uint32_t mode_count = 0;
2090 VkDisplayModePropertiesKHR *modes = NULL;
2091 unsigned dpy, i, j;
2092 uint32_t best_plane = UINT32_MAX;
2093 VkDisplayPlaneAlphaFlagBitsKHR alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
2094 VkDisplaySurfaceCreateInfoKHR create_info = { VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR };
2095 VkDisplayModeKHR best_mode = VK_NULL_HANDLE;
2096 /* Monitor index starts on 1, 0 is auto. */
2097 unsigned monitor_index = info->monitor_index;
2098 unsigned saved_width = *width;
2099 unsigned saved_height = *height;
2100
2101 /* We need to decide on GPU here to be able to query support. */
2102 if (!vulkan_context_init_gpu(vk))
2103 return false;
2104
2105 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance,
2106 vkGetPhysicalDeviceDisplayPropertiesKHR);
2107 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance,
2108 vkGetPhysicalDeviceDisplayPlanePropertiesKHR);
2109 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance,
2110 vkGetDisplayPlaneSupportedDisplaysKHR);
2111 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance,
2112 vkGetDisplayModePropertiesKHR);
2113 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance,
2114 vkCreateDisplayModeKHR);
2115 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance,
2116 vkGetDisplayPlaneCapabilitiesKHR);
2117 VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance,
2118 vkCreateDisplayPlaneSurfaceKHR);
2119
2120 #define GOTO_FAIL() do { \
2121 ret = false; \
2122 goto end; \
2123 } while(0)
2124
2125 if (vkGetPhysicalDeviceDisplayPropertiesKHR(vk->context.gpu, &display_count, NULL) != VK_SUCCESS)
2126 GOTO_FAIL();
2127 displays = (VkDisplayPropertiesKHR*)calloc(display_count, sizeof(*displays));
2128 if (!displays)
2129 GOTO_FAIL();
2130 if (vkGetPhysicalDeviceDisplayPropertiesKHR(vk->context.gpu, &display_count, displays) != VK_SUCCESS)
2131 GOTO_FAIL();
2132
2133 if (vkGetPhysicalDeviceDisplayPlanePropertiesKHR(vk->context.gpu, &plane_count, NULL) != VK_SUCCESS)
2134 GOTO_FAIL();
2135 planes = (VkDisplayPlanePropertiesKHR*)calloc(plane_count, sizeof(*planes));
2136 if (!planes)
2137 GOTO_FAIL();
2138 if (vkGetPhysicalDeviceDisplayPlanePropertiesKHR(vk->context.gpu, &plane_count, planes) != VK_SUCCESS)
2139 GOTO_FAIL();
2140
2141 if (monitor_index > display_count)
2142 {
2143 RARCH_WARN("Monitor index is out of range, using automatic display.\n");
2144 monitor_index = 0;
2145 }
2146
2147 retry:
2148 for (dpy = 0; dpy < display_count; dpy++)
2149 {
2150 VkDisplayKHR display;
2151 if (monitor_index != 0 && (monitor_index - 1) != dpy)
2152 continue;
2153
2154 display = displays[dpy].display;
2155 best_mode = VK_NULL_HANDLE;
2156 best_plane = UINT32_MAX;
2157
2158 if (vkGetDisplayModePropertiesKHR(vk->context.gpu,
2159 display, &mode_count, NULL) != VK_SUCCESS)
2160 GOTO_FAIL();
2161
2162 modes = (VkDisplayModePropertiesKHR*)calloc(mode_count, sizeof(*modes));
2163 if (!modes)
2164 GOTO_FAIL();
2165
2166 if (vkGetDisplayModePropertiesKHR(vk->context.gpu,
2167 display, &mode_count, modes) != VK_SUCCESS)
2168 GOTO_FAIL();
2169
2170 for (i = 0; i < mode_count; i++)
2171 {
2172 const VkDisplayModePropertiesKHR *mode = &modes[i];
2173 if (vulkan_update_display_mode(width, height, mode, info))
2174 best_mode = modes[i].displayMode;
2175 }
2176
2177 free(modes);
2178 modes = NULL;
2179 mode_count = 0;
2180
2181 if (best_mode == VK_NULL_HANDLE)
2182 continue;
2183
2184 for (i = 0; i < plane_count; i++)
2185 {
2186 uint32_t supported_count = 0;
2187 VkDisplayKHR *supported = NULL;
2188 VkDisplayPlaneCapabilitiesKHR plane_caps;
2189 vkGetDisplayPlaneSupportedDisplaysKHR(vk->context.gpu, i, &supported_count, NULL);
2190 if (!supported_count)
2191 continue;
2192
2193 supported = (VkDisplayKHR*)calloc(supported_count, sizeof(*supported));
2194 if (!supported)
2195 GOTO_FAIL();
2196
2197 vkGetDisplayPlaneSupportedDisplaysKHR(vk->context.gpu, i, &supported_count,
2198 supported);
2199
2200 for (j = 0; j < supported_count; j++)
2201 {
2202 if (supported[j] == display)
2203 {
2204 if (best_plane == UINT32_MAX)
2205 best_plane = j;
2206 break;
2207 }
2208 }
2209
2210 free(supported);
2211 supported = NULL;
2212
2213 if (j == supported_count)
2214 continue;
2215
2216 if (planes[i].currentDisplay == VK_NULL_HANDLE ||
2217 planes[i].currentDisplay == display)
2218 best_plane = j;
2219 else
2220 continue;
2221
2222 vkGetDisplayPlaneCapabilitiesKHR(vk->context.gpu,
2223 best_mode, i, &plane_caps);
2224
2225 if (plane_caps.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR)
2226 {
2227 best_plane = j;
2228 alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
2229 goto out;
2230 }
2231 }
2232 }
2233 out:
2234
2235 if (best_plane == UINT32_MAX && monitor_index != 0)
2236 {
2237 RARCH_WARN("Could not find suitable surface for monitor index: %u.\n",
2238 monitor_index);
2239 RARCH_WARN("Retrying first suitable monitor.\n");
2240 monitor_index = 0;
2241 best_mode = VK_NULL_HANDLE;
2242 *width = saved_width;
2243 *height = saved_height;
2244 goto retry;
2245 }
2246
2247 if (best_mode == VK_NULL_HANDLE)
2248 GOTO_FAIL();
2249 if (best_plane == UINT32_MAX)
2250 GOTO_FAIL();
2251
2252 create_info.displayMode = best_mode;
2253 create_info.planeIndex = best_plane;
2254 create_info.planeStackIndex = planes[best_plane].currentStackIndex;
2255 create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
2256 create_info.globalAlpha = 1.0f;
2257 create_info.alphaMode = alpha_mode;
2258 create_info.imageExtent.width = *width;
2259 create_info.imageExtent.height = *height;
2260
2261 if (vkCreateDisplayPlaneSurfaceKHR(vk->context.instance,
2262 &create_info, NULL, &vk->vk_surface) != VK_SUCCESS)
2263 GOTO_FAIL();
2264
2265 end:
2266 free(displays);
2267 free(planes);
2268 free(modes);
2269 return ret;
2270 }
2271
vulkan_surface_create(gfx_ctx_vulkan_data_t * vk,enum vulkan_wsi_type type,void * display,void * surface,unsigned width,unsigned height,unsigned swap_interval)2272 bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk,
2273 enum vulkan_wsi_type type,
2274 void *display, void *surface,
2275 unsigned width, unsigned height,
2276 unsigned swap_interval)
2277 {
2278 switch (type)
2279 {
2280 case VULKAN_WSI_WAYLAND:
2281 #ifdef HAVE_WAYLAND
2282 {
2283 PFN_vkCreateWaylandSurfaceKHR create;
2284 if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(vk->context.instance, "vkCreateWaylandSurfaceKHR", create))
2285 return false;
2286 VkWaylandSurfaceCreateInfoKHR surf_info;
2287
2288 memset(&surf_info, 0, sizeof(surf_info));
2289
2290 surf_info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
2291 surf_info.pNext = NULL;
2292 surf_info.flags = 0;
2293 surf_info.display = (struct wl_display*)display;
2294 surf_info.surface = (struct wl_surface*)surface;
2295
2296 if (create(vk->context.instance,
2297 &surf_info, NULL, &vk->vk_surface) != VK_SUCCESS)
2298 return false;
2299 }
2300 #endif
2301 break;
2302 case VULKAN_WSI_ANDROID:
2303 #ifdef ANDROID
2304 {
2305 PFN_vkCreateAndroidSurfaceKHR create;
2306 if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(vk->context.instance, "vkCreateAndroidSurfaceKHR", create))
2307 return false;
2308 VkAndroidSurfaceCreateInfoKHR surf_info;
2309
2310 memset(&surf_info, 0, sizeof(surf_info));
2311
2312 surf_info.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
2313 surf_info.flags = 0;
2314 surf_info.window = (ANativeWindow*)surface;
2315
2316 if (create(vk->context.instance,
2317 &surf_info, NULL, &vk->vk_surface) != VK_SUCCESS)
2318 {
2319 RARCH_ERR("[Vulkan]: Failed to create Android surface.\n");
2320 return false;
2321 }
2322 RARCH_LOG("[Vulkan]: Created Android surface: %llu\n",
2323 (unsigned long long)vk->vk_surface);
2324 }
2325 #endif
2326 break;
2327 case VULKAN_WSI_WIN32:
2328 #ifdef _WIN32
2329 {
2330 VkWin32SurfaceCreateInfoKHR surf_info;
2331 PFN_vkCreateWin32SurfaceKHR create;
2332
2333 if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(vk->context.instance, "vkCreateWin32SurfaceKHR", create))
2334 return false;
2335
2336 memset(&surf_info, 0, sizeof(surf_info));
2337
2338 surf_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
2339 surf_info.flags = 0;
2340 surf_info.hinstance = *(const HINSTANCE*)display;
2341 surf_info.hwnd = *(const HWND*)surface;
2342
2343 if (create(vk->context.instance,
2344 &surf_info, NULL, &vk->vk_surface) != VK_SUCCESS)
2345 return false;
2346 }
2347 #endif
2348 break;
2349 case VULKAN_WSI_XLIB:
2350 #ifdef HAVE_XLIB
2351 {
2352 PFN_vkCreateXlibSurfaceKHR create;
2353 if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(vk->context.instance, "vkCreateXlibSurfaceKHR", create))
2354 return false;
2355 VkXlibSurfaceCreateInfoKHR surf_info;
2356
2357 memset(&surf_info, 0, sizeof(surf_info));
2358
2359 surf_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
2360 surf_info.flags = 0;
2361 surf_info.dpy = (Display*)display;
2362 surf_info.window = *(const Window*)surface;
2363
2364 if (create(vk->context.instance,
2365 &surf_info, NULL, &vk->vk_surface)
2366 != VK_SUCCESS)
2367 return false;
2368 }
2369 #endif
2370 break;
2371 case VULKAN_WSI_XCB:
2372 #ifdef HAVE_X11
2373 #ifdef HAVE_XCB
2374 {
2375 PFN_vkCreateXcbSurfaceKHR create;
2376 if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(vk->context.instance, "vkCreateXcbSurfaceKHR", create))
2377 return false;
2378 VkXcbSurfaceCreateInfoKHR surf_info;
2379
2380 memset(&surf_info, 0, sizeof(surf_info));
2381
2382 surf_info.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
2383 surf_info.flags = 0;
2384 surf_info.connection = XGetXCBConnection((Display*)display);
2385 surf_info.window = *(const xcb_window_t*)surface;
2386
2387 if (create(vk->context.instance,
2388 &surf_info, NULL, &vk->vk_surface)
2389 != VK_SUCCESS)
2390 return false;
2391 }
2392 #endif
2393 #endif
2394 break;
2395 case VULKAN_WSI_MIR:
2396 #ifdef HAVE_MIR
2397 {
2398 PFN_vkCreateMirSurfaceKHR create;
2399 if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(vk->context.instance, "vkCreateMirSurfaceKHR", create))
2400 return false;
2401 VkMirSurfaceCreateInfoKHR surf_info;
2402
2403 memset(&surf_info, 0, sizeof(surf_info));
2404
2405 surf_info.sType = VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR;
2406 surf_info.connection = display;
2407 surf_info.mirSurface = surface;
2408
2409 if (create(vk->context.instance,
2410 &surf_info, NULL, &vk->vk_surface)
2411 != VK_SUCCESS)
2412 return false;
2413 }
2414 #endif
2415 break;
2416 case VULKAN_WSI_DISPLAY:
2417 {
2418 if (!vulkan_create_display_surface(vk,
2419 &width, &height,
2420 (const struct vulkan_display_surface_info*)display))
2421 return false;
2422 }
2423 break;
2424 case VULKAN_WSI_MVK_MACOS:
2425 #ifdef HAVE_COCOA
2426 {
2427 PFN_vkCreateMacOSSurfaceMVK create;
2428 if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(vk->context.instance, "vkCreateMacOSSurfaceMVK", create))
2429 return false;
2430 VkMacOSSurfaceCreateInfoMVK surf_info;
2431
2432 memset(&surf_info, 0, sizeof(surf_info));
2433
2434 surf_info.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
2435 surf_info.pNext = NULL;
2436 surf_info.flags = 0;
2437 surf_info.pView = surface;
2438
2439 if (create(vk->context.instance, &surf_info, NULL, &vk->vk_surface)
2440 != VK_SUCCESS)
2441 return false;
2442 }
2443 #endif
2444 break;
2445 case VULKAN_WSI_MVK_IOS:
2446 #ifdef HAVE_COCOATOUCH
2447 {
2448 PFN_vkCreateIOSSurfaceMVK create;
2449 if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(vk->context.instance, "vkCreateIOSSurfaceMVK", create))
2450 return false;
2451 VkIOSSurfaceCreateInfoMVK surf_info;
2452
2453 memset(&surf_info, 0, sizeof(surf_info));
2454
2455 surf_info.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
2456 surf_info.pNext = NULL;
2457 surf_info.flags = 0;
2458 surf_info.pView = surface;
2459
2460 if (create(vk->context.instance, &surf_info, NULL, &vk->vk_surface)
2461 != VK_SUCCESS)
2462 return false;
2463 }
2464 #endif
2465 break;
2466 case VULKAN_WSI_NONE:
2467 default:
2468 return false;
2469 }
2470
2471 /* Must create device after surface since we need to be able to query queues to use for presentation. */
2472 if (!vulkan_context_init_device(vk))
2473 return false;
2474
2475 if (!vulkan_create_swapchain(
2476 vk, width, height, swap_interval))
2477 return false;
2478
2479 vulkan_acquire_next_image(vk);
2480 return true;
2481 }
2482
vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t * vk)2483 static void vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t *vk)
2484 {
2485 unsigned i;
2486
2487 vulkan_emulated_mailbox_deinit(&vk->mailbox);
2488 if (vk->swapchain != VK_NULL_HANDLE)
2489 {
2490 vkDeviceWaitIdle(vk->context.device);
2491 vkDestroySwapchainKHR(vk->context.device, vk->swapchain, NULL);
2492 memset(vk->context.swapchain_images, 0, sizeof(vk->context.swapchain_images));
2493 vk->swapchain = VK_NULL_HANDLE;
2494 vk->context.has_acquired_swapchain = false;
2495 }
2496
2497 for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++)
2498 {
2499 if (vk->context.swapchain_semaphores[i] != VK_NULL_HANDLE)
2500 vkDestroySemaphore(vk->context.device,
2501 vk->context.swapchain_semaphores[i], NULL);
2502 if (vk->context.swapchain_fences[i] != VK_NULL_HANDLE)
2503 vkDestroyFence(vk->context.device,
2504 vk->context.swapchain_fences[i], NULL);
2505 if (vk->context.swapchain_recycled_semaphores[i] != VK_NULL_HANDLE)
2506 vkDestroySemaphore(vk->context.device,
2507 vk->context.swapchain_recycled_semaphores[i], NULL);
2508 if (vk->context.swapchain_wait_semaphores[i] != VK_NULL_HANDLE)
2509 vkDestroySemaphore(vk->context.device,
2510 vk->context.swapchain_wait_semaphores[i], NULL);
2511 }
2512
2513 if (vk->context.swapchain_acquire_semaphore != VK_NULL_HANDLE)
2514 vkDestroySemaphore(vk->context.device,
2515 vk->context.swapchain_acquire_semaphore, NULL);
2516 vk->context.swapchain_acquire_semaphore = VK_NULL_HANDLE;
2517
2518 memset(vk->context.swapchain_semaphores, 0,
2519 sizeof(vk->context.swapchain_semaphores));
2520 memset(vk->context.swapchain_recycled_semaphores, 0,
2521 sizeof(vk->context.swapchain_recycled_semaphores));
2522 memset(vk->context.swapchain_wait_semaphores, 0,
2523 sizeof(vk->context.swapchain_wait_semaphores));
2524 memset(vk->context.swapchain_fences, 0,
2525 sizeof(vk->context.swapchain_fences));
2526 vk->context.num_recycled_acquire_semaphores = 0;
2527 }
2528
vulkan_present(gfx_ctx_vulkan_data_t * vk,unsigned index)2529 void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index)
2530 {
2531 VkPresentInfoKHR present;
2532 VkResult result = VK_SUCCESS;
2533 VkResult err = VK_SUCCESS;
2534
2535 present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
2536 present.pNext = NULL;
2537 present.waitSemaphoreCount = 1;
2538 present.pWaitSemaphores = &vk->context.swapchain_semaphores[index];
2539 present.swapchainCount = 1;
2540 present.pSwapchains = &vk->swapchain;
2541 present.pImageIndices = &index;
2542 present.pResults = &result;
2543
2544 /* Better hope QueuePresent doesn't block D: */
2545 #ifdef HAVE_THREADS
2546 slock_lock(vk->context.queue_lock);
2547 #endif
2548 err = vkQueuePresentKHR(vk->context.queue, &present);
2549
2550 /* VK_SUBOPTIMAL_KHR can be returned on
2551 * Android 10 when prerotate is not dealt with.
2552 * This is not an error we need to care about,
2553 * and we'll treat it as SUCCESS. */
2554 if (result == VK_SUBOPTIMAL_KHR)
2555 result = VK_SUCCESS;
2556 if (err == VK_SUBOPTIMAL_KHR)
2557 err = VK_SUCCESS;
2558
2559 #ifdef WSI_HARDENING_TEST
2560 trigger_spurious_error_vkresult(&err);
2561 #endif
2562
2563 if (err != VK_SUCCESS || result != VK_SUCCESS)
2564 {
2565 RARCH_LOG("[Vulkan]: QueuePresent failed, destroying swapchain.\n");
2566 vulkan_destroy_swapchain(vk);
2567 }
2568
2569 #ifdef HAVE_THREADS
2570 slock_unlock(vk->context.queue_lock);
2571 #endif
2572 }
2573
vulkan_context_destroy(gfx_ctx_vulkan_data_t * vk,bool destroy_surface)2574 void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk,
2575 bool destroy_surface)
2576 {
2577 if (!vk->context.instance)
2578 return;
2579
2580 if (vk->context.device)
2581 vkDeviceWaitIdle(vk->context.device);
2582
2583 vulkan_destroy_swapchain(vk);
2584
2585 if (destroy_surface && vk->vk_surface != VK_NULL_HANDLE)
2586 {
2587 vkDestroySurfaceKHR(vk->context.instance,
2588 vk->vk_surface, NULL);
2589 vk->vk_surface = VK_NULL_HANDLE;
2590 }
2591
2592 #ifdef VULKAN_DEBUG
2593 if (vk->context.debug_callback)
2594 vkDestroyDebugReportCallbackEXT(vk->context.instance, vk->context.debug_callback, NULL);
2595 #endif
2596
2597 if (video_driver_is_video_cache_context())
2598 {
2599 cached_device_vk = vk->context.device;
2600 cached_instance_vk = vk->context.instance;
2601 cached_destroy_device_vk = vk->context.destroy_device;
2602 }
2603 else
2604 {
2605 if (vk->context.device)
2606 {
2607 vkDestroyDevice(vk->context.device, NULL);
2608 vk->context.device = NULL;
2609 }
2610 if (vk->context.instance)
2611 {
2612 if (vk->context.destroy_device)
2613 vk->context.destroy_device();
2614
2615 vkDestroyInstance(vk->context.instance, NULL);
2616 vk->context.instance = NULL;
2617
2618 if (vulkan_library)
2619 {
2620 dylib_close(vulkan_library);
2621 vulkan_library = NULL;
2622 }
2623 }
2624 }
2625
2626 video_driver_set_gpu_api_devices(GFX_CTX_VULKAN_API, NULL);
2627 if (vk->gpu_list)
2628 {
2629 string_list_free(vk->gpu_list);
2630 vk->gpu_list = NULL;
2631 }
2632 }
2633
vulkan_recycle_acquire_semaphore(struct vulkan_context * ctx,VkSemaphore sem)2634 static void vulkan_recycle_acquire_semaphore(struct vulkan_context *ctx, VkSemaphore sem)
2635 {
2636 assert(ctx->num_recycled_acquire_semaphores < VULKAN_MAX_SWAPCHAIN_IMAGES);
2637 ctx->swapchain_recycled_semaphores[ctx->num_recycled_acquire_semaphores++] = sem;
2638 }
2639
vulkan_acquire_clear_fences(gfx_ctx_vulkan_data_t * vk)2640 static void vulkan_acquire_clear_fences(gfx_ctx_vulkan_data_t *vk)
2641 {
2642 unsigned i;
2643 for (i = 0; i < vk->context.num_swapchain_images; i++)
2644 {
2645 if (vk->context.swapchain_fences[i])
2646 {
2647 vkDestroyFence(vk->context.device,
2648 vk->context.swapchain_fences[i], NULL);
2649 vk->context.swapchain_fences[i] = VK_NULL_HANDLE;
2650 }
2651 vk->context.swapchain_fences_signalled[i] = false;
2652
2653 if (vk->context.swapchain_wait_semaphores[i])
2654 vulkan_recycle_acquire_semaphore(&vk->context, vk->context.swapchain_wait_semaphores[i]);
2655 vk->context.swapchain_wait_semaphores[i] = VK_NULL_HANDLE;
2656 }
2657
2658 vk->context.current_frame_index = 0;
2659 }
2660
vulkan_get_wsi_acquire_semaphore(struct vulkan_context * ctx)2661 static VkSemaphore vulkan_get_wsi_acquire_semaphore(struct vulkan_context *ctx)
2662 {
2663 VkSemaphore sem;
2664
2665 if (ctx->num_recycled_acquire_semaphores == 0)
2666 {
2667 VkSemaphoreCreateInfo sem_info;
2668
2669 sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
2670 sem_info.pNext = NULL;
2671 sem_info.flags = 0;
2672 vkCreateSemaphore(ctx->device, &sem_info, NULL,
2673 &ctx->swapchain_recycled_semaphores[ctx->num_recycled_acquire_semaphores++]);
2674 }
2675
2676 sem =
2677 ctx->swapchain_recycled_semaphores[--ctx->num_recycled_acquire_semaphores];
2678 ctx->swapchain_recycled_semaphores[ctx->num_recycled_acquire_semaphores] =
2679 VK_NULL_HANDLE;
2680 return sem;
2681 }
2682
vulkan_acquire_wait_fences(gfx_ctx_vulkan_data_t * vk)2683 static void vulkan_acquire_wait_fences(gfx_ctx_vulkan_data_t *vk)
2684 {
2685 unsigned index;
2686 VkFenceCreateInfo fence_info;
2687 VkFence *next_fence = NULL;
2688
2689 fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
2690 fence_info.pNext = NULL;
2691 fence_info.flags = 0;
2692
2693 /* Decouples the frame fence index from swapchain index. */
2694 vk->context.current_frame_index =
2695 (vk->context.current_frame_index + 1) %
2696 vk->context.num_swapchain_images;
2697
2698 index = vk->context.current_frame_index;
2699 next_fence = &vk->context.swapchain_fences[index];
2700
2701 if (*next_fence != VK_NULL_HANDLE)
2702 {
2703 if (vk->context.swapchain_fences_signalled[index])
2704 vkWaitForFences(vk->context.device, 1, next_fence, true, UINT64_MAX);
2705 vkResetFences(vk->context.device, 1, next_fence);
2706 }
2707 else
2708 vkCreateFence(vk->context.device, &fence_info, NULL, next_fence);
2709 vk->context.swapchain_fences_signalled[index] = false;
2710
2711 if (vk->context.swapchain_wait_semaphores[index] != VK_NULL_HANDLE)
2712 vulkan_recycle_acquire_semaphore(&vk->context, vk->context.swapchain_wait_semaphores[index]);
2713 vk->context.swapchain_wait_semaphores[index] = VK_NULL_HANDLE;
2714 }
2715
vulkan_create_wait_fences(gfx_ctx_vulkan_data_t * vk)2716 static void vulkan_create_wait_fences(gfx_ctx_vulkan_data_t *vk)
2717 {
2718 VkFenceCreateInfo fence_info =
2719 { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
2720
2721 unsigned i;
2722 for (i = 0; i < vk->context.num_swapchain_images; i++)
2723 {
2724 if (!vk->context.swapchain_fences[i])
2725 vkCreateFence(vk->context.device, &fence_info, NULL,
2726 &vk->context.swapchain_fences[i]);
2727 }
2728
2729 vk->context.current_frame_index = 0;
2730 }
2731
vulkan_acquire_next_image(gfx_ctx_vulkan_data_t * vk)2732 void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk)
2733 {
2734 unsigned index;
2735 VkResult err;
2736 VkFenceCreateInfo fence_info;
2737 VkSemaphoreCreateInfo sem_info;
2738 VkFence fence = VK_NULL_HANDLE;
2739 VkSemaphore semaphore = VK_NULL_HANDLE;
2740 bool is_retrying = false;
2741
2742 fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
2743 fence_info.pNext = NULL;
2744 fence_info.flags = 0;
2745
2746 sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
2747 sem_info.pNext = NULL;
2748 sem_info.flags = 0;
2749
2750 retry:
2751 if (vk->swapchain == VK_NULL_HANDLE)
2752 {
2753 /* We don't have a swapchain, try to create one now. */
2754 if (!vulkan_create_swapchain(vk, vk->context.swapchain_width,
2755 vk->context.swapchain_height, vk->context.swap_interval))
2756 {
2757 #ifdef VULKAN_DEBUG
2758 RARCH_ERR("[Vulkan]: Failed to create new swapchain.\n");
2759 #endif
2760 retro_sleep(20);
2761 return;
2762 }
2763
2764 if (vk->swapchain == VK_NULL_HANDLE)
2765 {
2766 /* We still don't have a swapchain, so just fake it ... */
2767 vk->context.current_swapchain_index = 0;
2768 vk->context.current_frame_index = 0;
2769 vulkan_acquire_clear_fences(vk);
2770 vulkan_acquire_wait_fences(vk);
2771 vk->context.invalid_swapchain = true;
2772 return;
2773 }
2774 }
2775
2776 retro_assert(!vk->context.has_acquired_swapchain);
2777
2778 if (vk->emulating_mailbox)
2779 {
2780 /* Non-blocking acquire. If we don't get a swapchain frame right away,
2781 * just skip rendering to the swapchain this frame, similar to what
2782 * MAILBOX would do. */
2783 if (vk->mailbox.swapchain == VK_NULL_HANDLE)
2784 err = VK_ERROR_OUT_OF_DATE_KHR;
2785 else
2786 err = vulkan_emulated_mailbox_acquire_next_image(
2787 &vk->mailbox, &vk->context.current_swapchain_index);
2788 }
2789 else
2790 {
2791 if (vk->use_wsi_semaphore)
2792 semaphore = vulkan_get_wsi_acquire_semaphore(&vk->context);
2793 else
2794 vkCreateFence(vk->context.device, &fence_info, NULL, &fence);
2795
2796 err = vkAcquireNextImageKHR(vk->context.device,
2797 vk->swapchain, UINT64_MAX,
2798 semaphore, fence, &vk->context.current_swapchain_index);
2799
2800 #ifdef ANDROID
2801 /* VK_SUBOPTIMAL_KHR can be returned on Android 10
2802 * when prerotate is not dealt with.
2803 * This is not an error we need to care about, and
2804 * we'll treat it as SUCCESS. */
2805 if (err == VK_SUBOPTIMAL_KHR)
2806 err = VK_SUCCESS;
2807 #endif
2808 }
2809
2810 if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR)
2811 {
2812 if (fence != VK_NULL_HANDLE)
2813 vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX);
2814 vk->context.has_acquired_swapchain = true;
2815
2816 if (vk->context.swapchain_acquire_semaphore)
2817 {
2818 #ifdef HAVE_THREADS
2819 slock_lock(vk->context.queue_lock);
2820 #endif
2821 RARCH_LOG("[Vulkan]: Destroying stale acquire semaphore.\n");
2822 vkDeviceWaitIdle(vk->context.device);
2823 vkDestroySemaphore(vk->context.device, vk->context.swapchain_acquire_semaphore, NULL);
2824 #ifdef HAVE_THREADS
2825 slock_unlock(vk->context.queue_lock);
2826 #endif
2827 }
2828 vk->context.swapchain_acquire_semaphore = semaphore;
2829 }
2830 else
2831 {
2832 vk->context.has_acquired_swapchain = false;
2833 if (semaphore)
2834 vulkan_recycle_acquire_semaphore(&vk->context, semaphore);
2835 }
2836
2837 #ifdef WSI_HARDENING_TEST
2838 trigger_spurious_error_vkresult(&err);
2839 #endif
2840
2841 if (fence != VK_NULL_HANDLE)
2842 vkDestroyFence(vk->context.device, fence, NULL);
2843
2844 switch (err)
2845 {
2846 case VK_NOT_READY:
2847 case VK_TIMEOUT:
2848 /* Do nothing. */
2849 break;
2850 case VK_ERROR_OUT_OF_DATE_KHR:
2851 case VK_SUBOPTIMAL_KHR:
2852 /* Throw away the old swapchain and try again. */
2853 vulkan_destroy_swapchain(vk);
2854
2855 if (is_retrying)
2856 {
2857 RARCH_ERR("[Vulkan]: Swapchain is out of date, trying to create new one. Have tried multiple times ...\n");
2858 retro_sleep(10);
2859 }
2860 else
2861 RARCH_ERR("[Vulkan]: Swapchain is out of date, trying to create new one.\n");
2862 is_retrying = true;
2863 vulkan_acquire_clear_fences(vk);
2864 goto retry;
2865 default:
2866 if (err != VK_SUCCESS)
2867 {
2868 /* We are screwed, don't try anymore. Maybe it will work later. */
2869 vulkan_destroy_swapchain(vk);
2870 RARCH_ERR("[Vulkan]: Failed to acquire from swapchain (err = %d).\n",
2871 (int)err);
2872 if (err == VK_ERROR_SURFACE_LOST_KHR)
2873 RARCH_ERR("[Vulkan]: Got VK_ERROR_SURFACE_LOST_KHR.\n");
2874 /* Force driver to reset swapchain image handles. */
2875 vk->context.invalid_swapchain = true;
2876 vulkan_acquire_clear_fences(vk);
2877 return;
2878 }
2879 break;
2880 }
2881
2882 index = vk->context.current_swapchain_index;
2883 if (vk->context.swapchain_semaphores[index] == VK_NULL_HANDLE)
2884 vkCreateSemaphore(vk->context.device, &sem_info,
2885 NULL, &vk->context.swapchain_semaphores[index]);
2886 vulkan_acquire_wait_fences(vk);
2887 }
2888
vulkan_create_swapchain(gfx_ctx_vulkan_data_t * vk,unsigned width,unsigned height,unsigned swap_interval)2889 bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
2890 unsigned width, unsigned height,
2891 unsigned swap_interval)
2892 {
2893 unsigned i;
2894 uint32_t format_count;
2895 uint32_t present_mode_count;
2896 uint32_t desired_swapchain_images;
2897 VkResult res;
2898 VkSurfaceCapabilitiesKHR surface_properties;
2899 VkSurfaceFormatKHR formats[256];
2900 VkPresentModeKHR present_modes[16];
2901 VkSurfaceFormatKHR format;
2902 VkExtent2D swapchain_size;
2903 VkSwapchainKHR old_swapchain;
2904 VkSurfaceTransformFlagBitsKHR pre_transform;
2905 VkSwapchainCreateInfoKHR info = {
2906 VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
2907 VkPresentModeKHR swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR;
2908 settings_t *settings = config_get_ptr();
2909 VkCompositeAlphaFlagBitsKHR composite = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
2910
2911 vkDeviceWaitIdle(vk->context.device);
2912 vulkan_acquire_clear_fences(vk);
2913
2914 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk->context.gpu,
2915 vk->vk_surface, &surface_properties);
2916
2917 /* Skip creation when window is minimized */
2918 if (!surface_properties.currentExtent.width &&
2919 !surface_properties.currentExtent.height)
2920 return false;
2921
2922 if (swap_interval == 0 && vk->emulate_mailbox)
2923 {
2924 swap_interval = 1;
2925 vk->emulating_mailbox = true;
2926 }
2927 else
2928 vk->emulating_mailbox = false;
2929
2930 vk->created_new_swapchain = true;
2931
2932 if (vk->swapchain != VK_NULL_HANDLE &&
2933 !vk->context.invalid_swapchain &&
2934 vk->context.swapchain_width == width &&
2935 vk->context.swapchain_height == height &&
2936 vk->context.swap_interval == swap_interval)
2937 {
2938 /* Do not bother creating a swapchain redundantly. */
2939 #ifdef VULKAN_DEBUG
2940 RARCH_LOG("[Vulkan]: Do not need to re-create swapchain.\n");
2941 #endif
2942 vulkan_create_wait_fences(vk);
2943
2944 if ( vk->emulating_mailbox
2945 && vk->mailbox.swapchain == VK_NULL_HANDLE)
2946 {
2947 vulkan_emulated_mailbox_init(
2948 &vk->mailbox, vk->context.device, vk->swapchain);
2949 vk->created_new_swapchain = false;
2950 return true;
2951 }
2952 else if (
2953 !vk->emulating_mailbox
2954 && vk->mailbox.swapchain != VK_NULL_HANDLE)
2955 {
2956 /* We are tearing down, and entering a state
2957 * where we are supposed to have
2958 * acquired an image, so block until we have acquired. */
2959 if (!vk->context.has_acquired_swapchain)
2960 {
2961 if (vk->mailbox.swapchain == VK_NULL_HANDLE)
2962 res = VK_ERROR_OUT_OF_DATE_KHR;
2963 else
2964 res = vulkan_emulated_mailbox_acquire_next_image_blocking(
2965 &vk->mailbox,
2966 &vk->context.current_swapchain_index);
2967 }
2968 else
2969 res = VK_SUCCESS;
2970
2971 vulkan_emulated_mailbox_deinit(&vk->mailbox);
2972
2973 if (res == VK_SUCCESS)
2974 {
2975 vk->context.has_acquired_swapchain = true;
2976 vk->created_new_swapchain = false;
2977 return true;
2978 }
2979
2980 /* We failed for some reason, so create a new swapchain. */
2981 vk->context.has_acquired_swapchain = false;
2982 }
2983 else
2984 {
2985 vk->created_new_swapchain = false;
2986 return true;
2987 }
2988 }
2989
2990 vulkan_emulated_mailbox_deinit(&vk->mailbox);
2991
2992 present_mode_count = 0;
2993 vkGetPhysicalDeviceSurfacePresentModesKHR(
2994 vk->context.gpu, vk->vk_surface,
2995 &present_mode_count, NULL);
2996 if (present_mode_count < 1 || present_mode_count > 16)
2997 {
2998 RARCH_ERR("[Vulkan]: Bogus present modes found.\n");
2999 return false;
3000 }
3001 vkGetPhysicalDeviceSurfacePresentModesKHR(
3002 vk->context.gpu, vk->vk_surface,
3003 &present_mode_count, present_modes);
3004
3005 #ifdef VULKAN_DEBUG
3006 for (i = 0; i < present_mode_count; i++)
3007 {
3008 RARCH_LOG("[Vulkan]: Swapchain supports present mode: %u.\n",
3009 present_modes[i]);
3010 }
3011 #endif
3012
3013 vk->context.swap_interval = swap_interval;
3014 for (i = 0; i < present_mode_count; i++)
3015 {
3016 if (!swap_interval && present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
3017 {
3018 swapchain_present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
3019 break;
3020 }
3021 else if (!swap_interval && present_modes[i]
3022 == VK_PRESENT_MODE_IMMEDIATE_KHR)
3023 {
3024 swapchain_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
3025 break;
3026 }
3027 else if (swap_interval && present_modes[i] == VK_PRESENT_MODE_FIFO_KHR)
3028 {
3029 /* Kind of tautological since FIFO must always be present. */
3030 swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR;
3031 break;
3032 }
3033 }
3034
3035 #ifdef VULKAN_DEBUG
3036 RARCH_LOG("[Vulkan]: Creating swapchain with present mode: %u\n",
3037 (unsigned)swapchain_present_mode);
3038 #endif
3039
3040 vkGetPhysicalDeviceSurfaceFormatsKHR(vk->context.gpu,
3041 vk->vk_surface, &format_count, NULL);
3042 vkGetPhysicalDeviceSurfaceFormatsKHR(vk->context.gpu,
3043 vk->vk_surface, &format_count, formats);
3044
3045 format.format = VK_FORMAT_UNDEFINED;
3046 if (format_count == 1 && formats[0].format == VK_FORMAT_UNDEFINED)
3047 {
3048 format = formats[0];
3049 format.format = VK_FORMAT_B8G8R8A8_UNORM;
3050 }
3051 else
3052 {
3053 if (format_count == 0)
3054 {
3055 RARCH_ERR("[Vulkan]: Surface has no formats.\n");
3056 return false;
3057 }
3058
3059 for (i = 0; i < format_count; i++)
3060 {
3061 if (
3062 formats[i].format == VK_FORMAT_R8G8B8A8_UNORM ||
3063 formats[i].format == VK_FORMAT_B8G8R8A8_UNORM ||
3064 formats[i].format == VK_FORMAT_A8B8G8R8_UNORM_PACK32)
3065 format = formats[i];
3066 }
3067
3068 if (format.format == VK_FORMAT_UNDEFINED)
3069 format = formats[0];
3070 }
3071
3072 if (surface_properties.currentExtent.width == -1)
3073 {
3074 swapchain_size.width = width;
3075 swapchain_size.height = height;
3076 }
3077 else
3078 swapchain_size = surface_properties.currentExtent;
3079
3080 #ifdef WSI_HARDENING_TEST
3081 if (trigger_spurious_error())
3082 {
3083 surface_properties.maxImageExtent.width = 0;
3084 surface_properties.maxImageExtent.height = 0;
3085 surface_properties.minImageExtent.width = 0;
3086 surface_properties.minImageExtent.height = 0;
3087 }
3088 #endif
3089
3090 /* Clamp swapchain size to boundaries. */
3091 if (swapchain_size.width > surface_properties.maxImageExtent.width)
3092 swapchain_size.width = surface_properties.maxImageExtent.width;
3093 if (swapchain_size.width < surface_properties.minImageExtent.width)
3094 swapchain_size.width = surface_properties.minImageExtent.width;
3095 if (swapchain_size.height > surface_properties.maxImageExtent.height)
3096 swapchain_size.height = surface_properties.maxImageExtent.height;
3097 if (swapchain_size.height < surface_properties.minImageExtent.height)
3098 swapchain_size.height = surface_properties.minImageExtent.height;
3099
3100 if (swapchain_size.width == 0 && swapchain_size.height == 0)
3101 {
3102 /* Cannot create swapchain yet, try again later. */
3103 if (vk->swapchain != VK_NULL_HANDLE)
3104 vkDestroySwapchainKHR(vk->context.device, vk->swapchain, NULL);
3105 vk->swapchain = VK_NULL_HANDLE;
3106 vk->context.swapchain_width = width;
3107 vk->context.swapchain_height = height;
3108 vk->context.num_swapchain_images = 1;
3109
3110 memset(vk->context.swapchain_images, 0, sizeof(vk->context.swapchain_images));
3111 RARCH_LOG("[Vulkan]: Cannot create a swapchain yet. Will try again later ...\n");
3112 return true;
3113 }
3114
3115 #ifdef VULKAN_DEBUG
3116 RARCH_LOG("[Vulkan]: Using swapchain size %u x %u.\n",
3117 swapchain_size.width, swapchain_size.height);
3118 #endif
3119
3120 /* Unless we have other reasons to clamp, we should prefer 3 images.
3121 * We hard sync against the swapchain, so if we have 2 images,
3122 * we would be unable to overlap CPU and GPU, which can get very slow
3123 * for GPU-rendered cores. */
3124 desired_swapchain_images = settings->uints.video_max_swapchain_images;
3125
3126 /* Clamp images requested to what is supported by the implementation. */
3127 if (desired_swapchain_images < surface_properties.minImageCount)
3128 desired_swapchain_images = surface_properties.minImageCount;
3129
3130 if ((surface_properties.maxImageCount > 0)
3131 && (desired_swapchain_images > surface_properties.maxImageCount))
3132 desired_swapchain_images = surface_properties.maxImageCount;
3133
3134 if (surface_properties.supportedTransforms
3135 & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
3136 pre_transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
3137 else
3138 pre_transform = surface_properties.currentTransform;
3139
3140 if (surface_properties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)
3141 composite = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
3142 else if (surface_properties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
3143 composite = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
3144 else if (surface_properties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
3145 composite = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
3146 else if (surface_properties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
3147 composite = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
3148
3149 old_swapchain = vk->swapchain;
3150
3151 info.surface = vk->vk_surface;
3152 info.minImageCount = desired_swapchain_images;
3153 info.imageFormat = format.format;
3154 info.imageColorSpace = format.colorSpace;
3155 info.imageExtent.width = swapchain_size.width;
3156 info.imageExtent.height = swapchain_size.height;
3157 info.imageArrayLayers = 1;
3158 info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
3159 info.preTransform = pre_transform;
3160 info.compositeAlpha = composite;
3161 info.presentMode = swapchain_present_mode;
3162 info.clipped = true;
3163 info.oldSwapchain = old_swapchain;
3164 info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
3165 | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
3166
3167 #ifdef _WIN32
3168 /* On Windows, do not try to reuse the swapchain.
3169 * It causes a lot of issues on nVidia for some reason. */
3170 info.oldSwapchain = VK_NULL_HANDLE;
3171 if (old_swapchain != VK_NULL_HANDLE)
3172 vkDestroySwapchainKHR(vk->context.device, old_swapchain, NULL);
3173 #endif
3174
3175 if (vkCreateSwapchainKHR(vk->context.device,
3176 &info, NULL, &vk->swapchain) != VK_SUCCESS)
3177 {
3178 RARCH_ERR("[Vulkan]: Failed to create swapchain.\n");
3179 return false;
3180 }
3181
3182 #ifndef _WIN32
3183 if (old_swapchain != VK_NULL_HANDLE)
3184 vkDestroySwapchainKHR(vk->context.device, old_swapchain, NULL);
3185 #endif
3186
3187 vk->context.swapchain_width = swapchain_size.width;
3188 vk->context.swapchain_height = swapchain_size.height;
3189
3190 /* Make sure we create a backbuffer format that is as we expect. */
3191 switch (format.format)
3192 {
3193 case VK_FORMAT_B8G8R8A8_SRGB:
3194 vk->context.swapchain_format = VK_FORMAT_B8G8R8A8_UNORM;
3195 vk->context.swapchain_is_srgb = true;
3196 break;
3197
3198 case VK_FORMAT_R8G8B8A8_SRGB:
3199 vk->context.swapchain_format = VK_FORMAT_R8G8B8A8_UNORM;
3200 vk->context.swapchain_is_srgb = true;
3201 break;
3202
3203 case VK_FORMAT_R8G8B8_SRGB:
3204 vk->context.swapchain_format = VK_FORMAT_R8G8B8_UNORM;
3205 vk->context.swapchain_is_srgb = true;
3206 break;
3207
3208 case VK_FORMAT_B8G8R8_SRGB:
3209 vk->context.swapchain_format = VK_FORMAT_B8G8R8_UNORM;
3210 vk->context.swapchain_is_srgb = true;
3211 break;
3212
3213 default:
3214 vk->context.swapchain_format = format.format;
3215 break;
3216 }
3217
3218 vkGetSwapchainImagesKHR(vk->context.device, vk->swapchain,
3219 &vk->context.num_swapchain_images, NULL);
3220 vkGetSwapchainImagesKHR(vk->context.device, vk->swapchain,
3221 &vk->context.num_swapchain_images, vk->context.swapchain_images);
3222
3223 #ifdef VULKAN_DEBUG
3224 RARCH_LOG("[Vulkan]: Got %u swapchain images.\n",
3225 vk->context.num_swapchain_images);
3226 #endif
3227
3228 /* Force driver to reset swapchain image handles. */
3229 vk->context.invalid_swapchain = true;
3230 vk->context.has_acquired_swapchain = false;
3231 vulkan_create_wait_fences(vk);
3232
3233 if (vk->emulating_mailbox)
3234 vulkan_emulated_mailbox_init(&vk->mailbox, vk->context.device, vk->swapchain);
3235
3236 return true;
3237 }
3238
vulkan_initialize_render_pass(VkDevice device,VkFormat format,VkRenderPass * render_pass)3239 void vulkan_initialize_render_pass(VkDevice device, VkFormat format,
3240 VkRenderPass *render_pass)
3241 {
3242 VkAttachmentReference color_ref;
3243 VkRenderPassCreateInfo rp_info;
3244 VkAttachmentDescription attachment;
3245 VkSubpassDescription subpass = {0};
3246
3247 rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
3248 rp_info.pNext = NULL;
3249 rp_info.flags = 0;
3250 rp_info.attachmentCount = 1;
3251 rp_info.pAttachments = &attachment;
3252 rp_info.subpassCount = 1;
3253 rp_info.pSubpasses = &subpass;
3254 rp_info.dependencyCount = 0;
3255 rp_info.pDependencies = NULL;
3256
3257 color_ref.attachment = 0;
3258 color_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
3259
3260 /* We will always write to the entire framebuffer,
3261 * so we don't really need to clear. */
3262 attachment.flags = 0;
3263 attachment.format = format;
3264 attachment.samples = VK_SAMPLE_COUNT_1_BIT;
3265 attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
3266 attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
3267 attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
3268 attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
3269 attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
3270 attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
3271
3272 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
3273 subpass.colorAttachmentCount = 1;
3274 subpass.pColorAttachments = &color_ref;
3275
3276 vkCreateRenderPass(device, &rp_info, NULL, render_pass);
3277 }
3278
vulkan_set_uniform_buffer(VkDevice device,VkDescriptorSet set,unsigned binding,VkBuffer buffer,VkDeviceSize offset,VkDeviceSize range)3279 void vulkan_set_uniform_buffer(
3280 VkDevice device,
3281 VkDescriptorSet set,
3282 unsigned binding,
3283 VkBuffer buffer,
3284 VkDeviceSize offset,
3285 VkDeviceSize range)
3286 {
3287 VkWriteDescriptorSet write;
3288 VkDescriptorBufferInfo buffer_info;
3289
3290 buffer_info.buffer = buffer;
3291 buffer_info.offset = offset;
3292 buffer_info.range = range;
3293
3294 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
3295 write.pNext = NULL;
3296 write.dstSet = set;
3297 write.dstBinding = binding;
3298 write.dstArrayElement = 0;
3299 write.descriptorCount = 1;
3300 write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
3301 write.pImageInfo = NULL;
3302 write.pBufferInfo = &buffer_info;
3303 write.pTexelBufferView = NULL;
3304
3305 vkUpdateDescriptorSets(device, 1, &write, 0, NULL);
3306 }
3307
vulkan_framebuffer_generate_mips(VkFramebuffer framebuffer,VkImage image,struct Size2D size,VkCommandBuffer cmd,unsigned levels)3308 void vulkan_framebuffer_generate_mips(
3309 VkFramebuffer framebuffer,
3310 VkImage image,
3311 struct Size2D size,
3312 VkCommandBuffer cmd,
3313 unsigned levels
3314 )
3315 {
3316 unsigned i;
3317 /* This is run every frame, so make sure
3318 * we aren't opting into the "lazy" way of doing this. :) */
3319 VkImageMemoryBarrier barriers[2] = {
3320 { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER },
3321 { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER },
3322 };
3323
3324 /* First, transfer the input mip level to TRANSFER_SRC_OPTIMAL.
3325 * This should allow the surface to stay compressed.
3326 * All subsequent mip-layers are now transferred into DST_OPTIMAL from
3327 * UNDEFINED at this point.
3328 */
3329
3330 /* Input */
3331 barriers[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
3332 barriers[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
3333 barriers[0].oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
3334 barriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
3335 barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3336 barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3337 barriers[0].image = image;
3338 barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3339 barriers[0].subresourceRange.baseMipLevel = 0;
3340 barriers[0].subresourceRange.levelCount = 1;
3341 barriers[0].subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
3342
3343 /* The rest of the mip chain */
3344 barriers[1].srcAccessMask = 0;
3345 barriers[1].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
3346 barriers[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
3347 barriers[1].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
3348 barriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3349 barriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3350 barriers[1].image = image;
3351 barriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3352 barriers[1].subresourceRange.baseMipLevel = 1;
3353 barriers[1].subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
3354 barriers[1].subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
3355
3356 vkCmdPipelineBarrier(cmd,
3357 VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
3358 VK_PIPELINE_STAGE_TRANSFER_BIT,
3359 false,
3360 0,
3361 NULL,
3362 0,
3363 NULL,
3364 2,
3365 barriers);
3366
3367 for (i = 1; i < levels; i++)
3368 {
3369 unsigned src_width, src_height, target_width, target_height;
3370 VkImageBlit blit_region = {{0}};
3371
3372 /* For subsequent passes, we have to transition
3373 * from DST_OPTIMAL to SRC_OPTIMAL,
3374 * but only do so one mip-level at a time. */
3375 if (i > 1)
3376 {
3377 barriers[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
3378 barriers[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
3379 barriers[0].subresourceRange.baseMipLevel = i - 1;
3380 barriers[0].subresourceRange.levelCount = 1;
3381 barriers[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
3382 barriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
3383
3384 vkCmdPipelineBarrier(cmd,
3385 VK_PIPELINE_STAGE_TRANSFER_BIT,
3386 VK_PIPELINE_STAGE_TRANSFER_BIT,
3387 false,
3388 0,
3389 NULL,
3390 0,
3391 NULL,
3392 1,
3393 barriers);
3394 }
3395
3396 src_width = MAX(size.width >> (i - 1), 1u);
3397 src_height = MAX(size.height >> (i - 1), 1u);
3398 target_width = MAX(size.width >> i, 1u);
3399 target_height = MAX(size.height >> i, 1u);
3400
3401 blit_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3402 blit_region.srcSubresource.mipLevel = i - 1;
3403 blit_region.srcSubresource.baseArrayLayer = 0;
3404 blit_region.srcSubresource.layerCount = 1;
3405 blit_region.dstSubresource = blit_region.srcSubresource;
3406 blit_region.dstSubresource.mipLevel = i;
3407 blit_region.srcOffsets[1].x = src_width;
3408 blit_region.srcOffsets[1].y = src_height;
3409 blit_region.srcOffsets[1].z = 1;
3410 blit_region.dstOffsets[1].x = target_width;
3411 blit_region.dstOffsets[1].y = target_height;
3412 blit_region.dstOffsets[1].z = 1;
3413
3414 vkCmdBlitImage(cmd,
3415 image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
3416 image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3417 1, &blit_region, VK_FILTER_LINEAR);
3418 }
3419
3420 /* We are now done, and we have all mip-levels except
3421 * the last in TRANSFER_SRC_OPTIMAL,
3422 * and the last one still on TRANSFER_DST_OPTIMAL,
3423 * so do a final barrier which
3424 * moves everything to SHADER_READ_ONLY_OPTIMAL in
3425 * one go along with the execution barrier to next pass.
3426 * Read-to-read memory barrier, so only need execution
3427 * barrier for first transition.
3428 */
3429 barriers[0].srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
3430 barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
3431 barriers[0].subresourceRange.baseMipLevel = 0;
3432 barriers[0].subresourceRange.levelCount = levels - 1;
3433 barriers[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
3434 barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
3435
3436 /* This is read-after-write barrier. */
3437 barriers[1].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
3438 barriers[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
3439 barriers[1].subresourceRange.baseMipLevel = levels - 1;
3440 barriers[1].subresourceRange.levelCount = 1;
3441 barriers[1].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
3442 barriers[1].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
3443
3444 vkCmdPipelineBarrier(cmd,
3445 VK_PIPELINE_STAGE_TRANSFER_BIT,
3446 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
3447 false,
3448 0,
3449 NULL,
3450 0,
3451 NULL,
3452 2, barriers);
3453
3454 /* Next pass will wait for ALL_GRAPHICS_BIT, and since
3455 * we have dstStage as FRAGMENT_SHADER,
3456 * the dependency chain will ensure we don't start
3457 * next pass until the mipchain is complete. */
3458 }
3459
vulkan_framebuffer_copy(VkImage image,struct Size2D size,VkCommandBuffer cmd,VkImage src_image,VkImageLayout src_layout)3460 void vulkan_framebuffer_copy(VkImage image,
3461 struct Size2D size,
3462 VkCommandBuffer cmd,
3463 VkImage src_image, VkImageLayout src_layout)
3464 {
3465 VkImageCopy region;
3466
3467 VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, image,VK_REMAINING_MIP_LEVELS,
3468 VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3469 0, VK_ACCESS_TRANSFER_WRITE_BIT,
3470 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
3471 VK_PIPELINE_STAGE_TRANSFER_BIT,
3472 VK_QUEUE_FAMILY_IGNORED,
3473 VK_QUEUE_FAMILY_IGNORED);
3474
3475 region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3476 region.srcSubresource.mipLevel = 0;
3477 region.srcSubresource.baseArrayLayer = 0;
3478 region.srcSubresource.layerCount = 1;
3479 region.srcOffset.x = 0;
3480 region.srcOffset.y = 0;
3481 region.srcOffset.z = 0;
3482 region.dstSubresource = region.srcSubresource;
3483 region.dstOffset.x = 0;
3484 region.dstOffset.y = 0;
3485 region.dstOffset.z = 0;
3486 region.extent.width = size.width;
3487 region.extent.height = size.height;
3488 region.extent.depth = 1;
3489
3490 vkCmdCopyImage(cmd,
3491 src_image, src_layout,
3492 image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3493 1, ®ion);
3494
3495 VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd,
3496 image,
3497 VK_REMAINING_MIP_LEVELS,
3498 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3499 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
3500 VK_ACCESS_TRANSFER_WRITE_BIT,
3501 VK_ACCESS_SHADER_READ_BIT,
3502 VK_PIPELINE_STAGE_TRANSFER_BIT,
3503 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
3504 VK_QUEUE_FAMILY_IGNORED,
3505 VK_QUEUE_FAMILY_IGNORED);
3506 }
3507
vulkan_framebuffer_clear(VkImage image,VkCommandBuffer cmd)3508 void vulkan_framebuffer_clear(VkImage image, VkCommandBuffer cmd)
3509 {
3510 VkClearColorValue color;
3511 VkImageSubresourceRange range;
3512
3513 VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd,
3514 image,
3515 VK_REMAINING_MIP_LEVELS,
3516 VK_IMAGE_LAYOUT_UNDEFINED,
3517 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3518 0,
3519 VK_ACCESS_TRANSFER_WRITE_BIT,
3520 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
3521 VK_PIPELINE_STAGE_TRANSFER_BIT,
3522 VK_QUEUE_FAMILY_IGNORED,
3523 VK_QUEUE_FAMILY_IGNORED);
3524
3525 color.float32[0] = 0.0f;
3526 color.float32[1] = 0.0f;
3527 color.float32[2] = 0.0f;
3528 color.float32[3] = 0.0f;
3529 range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3530 range.baseMipLevel = 0;
3531 range.levelCount = 1;
3532 range.baseArrayLayer = 0;
3533 range.layerCount = 1;
3534
3535 vkCmdClearColorImage(cmd,
3536 image,
3537 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3538 &color,
3539 1,
3540 &range);
3541
3542 VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd,
3543 image,
3544 VK_REMAINING_MIP_LEVELS,
3545 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3546 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
3547 VK_ACCESS_TRANSFER_WRITE_BIT,
3548 VK_ACCESS_SHADER_READ_BIT,
3549 VK_PIPELINE_STAGE_TRANSFER_BIT,
3550 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
3551 VK_QUEUE_FAMILY_IGNORED,
3552 VK_QUEUE_FAMILY_IGNORED);
3553 }
3554