0749f07b50d2c8bee322c42992a6552842d9d109
[vktest3] / src / vk.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdint.h>
4 #include <vulkan/vulkan.h>
5 #include "vk.h"
6 #include "util.h"
7 #include "darray.h"
8
9 #ifdef __WIN32__
10 #include <vulkan/vulkan_win32.h>
11 #else
12 /*#include <vulkan/vulkan_xlib.h>*/
13 #include <X11/Xlib-xcb.h>
14 #include <vulkan/vulkan_xcb.h>
15 #endif
16
17 static int create_instance(void);
18 static int create_surface(void);
19 static int choose_phys_dev(void);
20 static int create_device(void);
21 static int create_swapchain(void);
22
23 static int choose_pixfmt(void);
24 static int eval_pdev_score(VkPhysicalDevice dev);
25 static int have_inst_layer(const char *name);
26 static int have_ext(VkExtensionProperties *ext, int next, const char *name);
27
28 static Display *dpy;
29 static Window win;
30 static int initflags;
31
32 static VkInstance vk;
33 static VkPhysicalDevice vkpdev;
34 static int vkqfam_idx, vkqfam_maxq;
35 static VkDevice vkdev;
36 static VkQueue vkq;
37 static VkSurfaceKHR vksurf;
38 static VkSurfaceCapabilitiesKHR vksurf_caps;
39 static int vksurf_numfmt, vksurf_selfmt;
40 static VkSurfaceFormatKHR *vksurf_fmt;
41 static VkSwapchainKHR vksc;
42 static int vksc_numimg;
43 static VkImage *vksc_img;
44 static VkExtent2D vksc_extent;
45 static VkImageView *vksc_view;
46
47 static VkLayerProperties *inst_layers;
48 static VkExtensionProperties *inst_ext, *dev_ext;
49 static uint32_t inst_ext_count, dev_ext_count, inst_layers_count;
50
51 static VkPhysicalDevice *pdev_list;
52 static uint32_t num_pdev;
53
54 static int have_raytrace, have_debug_report;
55
56 void vk_init_xwin(Display *d, Window w)
57 {
58         dpy = d;
59         win = w;
60 }
61
62 int vk_init(unsigned int flags, unsigned int *usedflags)
63 {
64         initflags = flags;
65         if(create_instance() == -1)     return -1;
66         if(create_surface() == -1) return -1;
67         if(choose_phys_dev() == -1) return -1;
68         if(create_device() == -1) return -1;
69
70         if(initflags != flags) {
71                 if(usedflags) {
72                         *usedflags = initflags;
73                 } else {
74                         vk_cleanup();
75                         return -1;
76                 }
77         }
78         return 0;
79 }
80
81 void vk_cleanup(void)
82 {
83         int i;
84
85         free(vksc_img);
86         vksc_img = 0;
87         free(vksc_view);
88         vksc_view = 0;
89         if(vksc_view) {
90                 for(i=0; i<vksc_numimg; i++) {
91                         vkDestroyImageView(vkdev, vksc_view[i], 0);
92                 }
93                 vksc_view = 0;
94         }
95         if(vksc) {
96                 vkDestroySwapchainKHR(vkdev, vksc, 0);
97                 vksc = 0;
98         }
99         if(vkdev) {
100                 vkDestroyDevice(vkdev, 0);
101                 vkdev = 0;
102         }
103         if(vksurf) {
104                 vkDestroySurfaceKHR(vk, vksurf, 0);
105                 vksurf = 0;
106         }
107         if(vk) {
108                 vkDestroyInstance(vk, 0);
109                 vk = 0;
110         }
111         free(inst_layers);
112         inst_layers = 0;
113         free(inst_ext);
114         inst_ext = 0;
115         free(dev_ext);
116         dev_ext = 0;
117         free(pdev_list);
118         pdev_list = 0;
119 }
120
121 int vk_reshape(int xsz, int ysz)
122 {
123         int i;
124
125         if(vksc && vksc_extent.width == xsz && vksc_extent.height == ysz) {
126                 return 0;
127         }
128
129         if(vksc_view) {
130                 for(i=0; i<vksc_numimg; i++) {
131                         vkDestroyImageView(vkdev, vksc_view[i], 0);
132                 }
133         }
134         if(vksc) vkDestroySwapchainKHR(vkdev, vksc, 0);
135
136         vksc_extent.width = xsz;
137         vksc_extent.height = ysz;
138
139         if(create_swapchain() == -1) return -1;
140
141         /* TODO create depth/stencil buffers as needed (initflags) */
142         return 0;
143 }
144
145 struct rpass {
146         int used;
147         int fmt;
148         int zfmt;
149         int num_colbuf;
150         int num_samples;
151         /* TODO: stuff about depth-stencil */
152         int clear;
153
154         int vkobj_valid;
155         VkRenderPass vkobj;
156 };
157
158 static struct rpass *rpasses;
159
160 int vk_create_rpass(void)
161 {
162         int i;
163         struct rpass rpass = {0}, *rp = &rpass;
164
165         if(!rpasses) {
166                 rpasses = darr_alloc(0, sizeof *rpasses);
167                 darr_push(rpasses, &rpass);     /* add dummy rpass */
168         }
169
170         for(i=1; i<darr_size(rpasses); i++) {
171                 if(!rpasses[i].used) {
172                         rp = rpasses + i;
173                 }
174         }
175
176         /* init renderpass defaults */
177         rp->used = 1;
178         rp->fmt = vksurf_fmt[vksurf_selfmt].format;
179         rp->zfmt = VK_FORMAT_D24_UNORM_S8_UINT;
180         rp->num_colbuf = 1;
181         rp->num_samples = 1;
182         rp->clear = 1;
183         rp->vkobj_valid = 0;
184         rp->vkobj = 0;
185
186         if(rp == &rpass) {
187                 darr_push(rpasses, rp);
188                 return darr_size(rpasses) - 1;
189         }
190         return rp - rpasses;
191 }
192
193 void vk_free_rpass(int rp)
194 {
195         if(!rpasses || rp < 1 || rp >= darr_size(rpasses)) {
196                 return;
197         }
198
199         if(rpasses[rp].used && rpasses[rp].vkobj) {
200                 vkDestroyRenderPass(vkdev, rpasses[rp].vkobj, 0);
201         }
202         rpasses[rp].used = 0;
203 }
204
205 void vk_rpass_colorbuf(int rp, int fmt, int n)
206 {
207         rpasses[rp].fmt = fmt;
208         rpasses[rp].num_colbuf = n;
209         rpasses[rp].vkobj_valid = 0;
210 }
211
212 void vk_rpass_msaa(int rp, int nsamp)
213 {
214         rpasses[rp].num_samples = nsamp;
215 }
216
217 void vk_rpass_clear(int rp, int clear)
218 {
219         rpasses[rp].clear = clear;
220 }
221
222 VkRenderPass vk_rpass(int rp)
223 {
224         int i, zidx;
225         struct rpass *r;
226         VkAttachmentDescription att[17];
227         VkAttachmentReference catref[16], zatref;
228         VkSubpassDescription subpass;
229         VkRenderPassCreateInfo pinf;
230
231         r = rpasses + rp;
232
233         if(!r->vkobj_valid) {
234                 zidx = r->num_colbuf;
235                 memset(att, 0, sizeof att);
236                 for(i=0; i<r->num_colbuf; i++) {
237                         att[i].format = r->fmt;
238                         att[i].samples = r->num_samples;
239                         att[i].loadOp = r->clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
240                         att[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
241                         att[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
242                         att[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
243                         att[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
244                         att[i].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
245                 }
246                 att[zidx].format = r->zfmt;
247                 att[zidx].samples = 1;
248                 att[zidx].loadOp = r->clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
249                 att[zidx].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
250                 att[zidx].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
251                 att[zidx].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
252                 att[zidx].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
253                 att[zidx].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
254
255                 for(i=0; i<r->num_colbuf; i++) {
256                         catref[i].attachment = i;
257                         catref[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
258                 }
259                 zatref.attachment = zidx;
260                 zatref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
261
262                 memset(&subpass, 0, sizeof subpass);
263                 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
264                 subpass.colorAttachmentCount = r->num_colbuf;
265                 subpass.pColorAttachments = catref;
266                 subpass.pDepthStencilAttachment = &zatref;
267
268                 memset(&pinf, 0, sizeof pinf);
269                 pinf.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
270                 pinf.attachmentCount = r->num_colbuf + 1;
271                 pinf.pAttachments = att;
272                 pinf.subpassCount = 1;
273                 pinf.pSubpasses = &subpass;
274
275                 if(vkCreateRenderPass(vkdev, &pinf, 0, &r->vkobj) != 0) {
276                         fprintf(stderr, "failed to create render pass!\n");
277                         return 0;
278                 }
279                 r->vkobj_valid = 1;
280         }
281
282         return r->vkobj;
283 }
284
285 #define ARRSZ(arr)      (sizeof arr / sizeof *arr)
286 static const char *known_layer_list[] = {
287         "VK_LAYER_GOOGLE_threading",
288         "VK_LAYER_LUNARG_parameter_validation",
289         "VK_LAYER_LUNARG_object_tracker",
290         "VK_LAYER_LUNARG_image",
291         "VK_LAYER_LUNARG_core_validation",
292         "VK_LAYER_LUNARG_swapchain",
293         "VK_LAYER_GOOGLE_unique_objects"
294 };
295
296 static struct {
297         const char *name;
298         int required;
299 } known_instext_list[] = {
300         {"VK_KHR_surface", 1},
301 #ifdef __WIN32__
302         {"VK_KHR_win32_surface", 1},
303 #else
304         /*{"VK_KHR_xlib_surface", 1},*/
305         {"VK_KHR_xcb_surface", 1},
306 #endif
307         {"VK_KHR_debug_report", 0}
308 };
309
310 static struct {
311         const char *name;
312         int required;
313 } known_devext_list[] = {
314         {"VK_KHR_swapchain", 1},
315         {"VK_KHR_acceleration_structure", 0},
316         {"VK_KHR_ray_tracing_pipeline", 0}
317 };
318
319 static int create_instance(void)
320 {
321         int i, nlayers = 0, next = 0;
322         VkInstanceCreateInfo instinf;
323         VkApplicationInfo appinf;
324         const char *layers[ARRSZ(known_layer_list)];
325         const char *ext[ARRSZ(known_instext_list)];
326         uint32_t apiver;
327
328         vkEnumerateInstanceVersion(&apiver);
329         printf("Vulkan API version: %d.%d.%d\n", (apiver >> 22) & 0x7f,
330                         (apiver >> 12) & 0x3ff, apiver & 0xfff);
331
332         memset(&appinf, 0, sizeof appinf);
333         appinf.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
334         appinf.pApplicationName = "vkray";
335         appinf.pEngineName = "vkray";
336         appinf.apiVersion = apiver;
337
338         vkEnumerateInstanceLayerProperties(&inst_layers_count, 0);
339         inst_layers = malloc_nf(inst_layers_count * sizeof *inst_layers);
340         vkEnumerateInstanceLayerProperties(&inst_layers_count, inst_layers);
341
342         vkEnumerateInstanceExtensionProperties(0, &inst_ext_count, 0);
343         inst_ext = malloc_nf(inst_ext_count * sizeof *inst_ext);
344         vkEnumerateInstanceExtensionProperties(0, &inst_ext_count, inst_ext);
345
346         printf("Layers:\n");
347         for(i=0; i<inst_layers_count; i++) {
348                 printf(" - %s: %s\n", inst_layers[i].layerName, inst_layers[i].description);
349         }
350         printf("Instance extensions:\n");
351         for(i=0; i<inst_ext_count; i++) {
352                 printf(" - %s\n", inst_ext[i].extensionName);
353         }
354
355         have_debug_report = have_ext(inst_ext, inst_ext_count, "VK_KHR_debug_report");
356
357         for(i=0; i<ARRSZ(known_layer_list); i++) {
358                 if(have_inst_layer(known_layer_list[i])) {
359                         layers[nlayers++] = known_layer_list[i];
360                 }
361         }
362         for(i=0; i<ARRSZ(known_instext_list); i++) {
363                 if(have_ext(inst_ext, inst_ext_count, known_instext_list[i].name)) {
364                         ext[next++] = known_instext_list[i].name;
365                 } else if(known_instext_list[i].required) {
366                         fprintf(stderr, "Vulkan implementation lacks required instance extension: %s\n",
367                                         known_instext_list[i].name);
368                         return -1;
369                 }
370         }
371
372         memset(&instinf, 0, sizeof instinf);
373         instinf.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
374         instinf.pApplicationInfo = &appinf;
375         instinf.enabledLayerCount = nlayers;
376         instinf.ppEnabledLayerNames = layers;
377         instinf.enabledExtensionCount = next;
378         instinf.ppEnabledExtensionNames = ext;
379         if(vkCreateInstance(&instinf, 0, &vk) != 0) {
380                 fprintf(stderr, "failed to create vulkan instance\n");
381                 return -1;
382         }
383
384         return 0;
385 }
386
387 static int create_surface(void)
388 {
389         /*
390         VkXlibSurfaceCreateInfoKHR xinf = {0};
391         xinf.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
392         xinf.dpy = dpy;
393         xinf.window = win;
394
395         if(vkCreateXlibSurfaceKHR(vk, &xinf, 0, &vksurf) != 0) {
396                 fprintf(stderr, "failed to create Xlib window surface\n");
397                 return -1;
398         }
399         */
400         VkXcbSurfaceCreateInfoKHR xinf = {0};
401         xinf.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
402         xinf.connection = XGetXCBConnection(dpy);
403         xinf.window = (xcb_window_t)win;
404
405         if(vkCreateXcbSurfaceKHR(vk, &xinf, 0, &vksurf) != 0) {
406                 fprintf(stderr, "failed to create XCB window surface\n");
407                 return -1;
408         }
409         return 0;
410 }
411
412 int choose_phys_dev(void)
413 {
414         uint32_t i, num_pdev, num_qfam, score, best_score, best_dev;
415         VkPhysicalDevice *pdev;
416         VkPhysicalDeviceProperties pdevprop;
417         VkQueueFamilyProperties *qfam;
418         VkBool32 can_pres;
419
420         vkEnumeratePhysicalDevices(vk, &num_pdev, 0);
421         if(!num_pdev) {
422                 fprintf(stderr, "no vulkan devices found\n");
423                 return -1;
424         }
425         pdev = malloc_nf(num_pdev * sizeof *pdev);
426         vkEnumeratePhysicalDevices(vk, &num_pdev, pdev);
427
428         printf("Found %d physical devices\n", num_pdev);
429
430         best_score = 0;
431         best_dev = -1;
432         for(i=0; i<num_pdev; i++) {
433                 if((score = eval_pdev_score(pdev[i])) && score > best_score) {
434                         best_score = score;
435                         best_dev = i;
436                 }
437
438                 vkGetPhysicalDeviceProperties(pdev[i], &pdevprop);
439                 printf(" %d: %s (score: %d)\n", i, pdevprop.deviceName, score);
440         }
441         if(best_dev == -1) {
442                 fprintf(stderr, "no suitable vulkan device found\n");
443                 free(pdev);
444                 return -1;
445         }
446         vkpdev = pdev[best_dev];
447
448         vkGetPhysicalDeviceQueueFamilyProperties(vkpdev, &num_qfam, 0);
449         qfam = malloc_nf(num_qfam * sizeof *qfam);
450         vkGetPhysicalDeviceQueueFamilyProperties(vkpdev, &num_qfam, qfam);
451
452         vkqfam_idx = -1;
453         for(i=0; i<num_qfam; i++) {
454                 vkGetPhysicalDeviceSurfaceSupportKHR(vkpdev, i, vksurf, &can_pres);
455                 if(qfam[i].queueCount && (qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && can_pres) {
456                         vkqfam_maxq = qfam[i].queueCount;
457                         vkqfam_idx = i;
458                         break;
459                 }
460         }
461
462         free(qfam);
463         free(pdev);
464         choose_pixfmt();
465         return 0;
466 }
467
468 static int create_device(void)
469 {
470         float prio = 1.0f;
471         VkDeviceQueueCreateInfo qinf = {0};
472         VkPhysicalDeviceFeatures feat = {0};
473         VkDeviceCreateInfo devinf = {0};
474         const char *ext[ARRSZ(known_devext_list) + 16];
475         int i, num_ext;
476
477         vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, 0);
478         dev_ext = malloc_nf(dev_ext_count * sizeof *dev_ext);
479         vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, dev_ext);
480
481         num_ext = 0;
482         for(i=0; i<ARRSZ(known_devext_list); i++) {
483                 if(have_ext(dev_ext, dev_ext_count, known_devext_list[i].name)) {
484                         ext[num_ext++] = known_devext_list[i].name;
485                 } else if(known_devext_list[i].required) {
486                         fprintf(stderr, "Vulkan device lacks required extension: %s\n",
487                                         known_devext_list[i].name);
488                         return -1;
489                 }
490         }
491
492         if(initflags & VKINIT_RAY) {
493                 if(have_ext(dev_ext, dev_ext_count, "VK_KHR_acceleration_structure") &&
494                                 have_ext(dev_ext, dev_ext_count, "VK_KHR_ray_tracing_pipeline")) {
495                         ext[num_ext++] = "VK_KHR_acceleration_structure";
496                         ext[num_ext++] = "VK_KHR_ray_tracing_pipeline";
497                 } else {
498                         initflags &= ~VKINIT_RAY;
499                 }
500         }
501
502         qinf.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
503         qinf.queueFamilyIndex = vkqfam_idx;
504         qinf.queueCount = 1;
505         qinf.pQueuePriorities = &prio;
506
507         devinf.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
508         devinf.pQueueCreateInfos = &qinf;
509         devinf.queueCreateInfoCount = 1;
510         devinf.pEnabledFeatures = &feat;
511         devinf.enabledExtensionCount = num_ext;
512         devinf.ppEnabledExtensionNames = ext;
513
514         if(vkCreateDevice(vkpdev, &devinf, 0, &vkdev) != 0) {
515                 fprintf(stderr, "failed to create vulkan device\n");
516                 return -1;
517         }
518
519         vkGetDeviceQueue(vkdev, vkqfam_idx, 0, &vkq);
520         return 0;
521 }
522
523 static int create_swapchain(void)
524 {
525         int i;
526         uint32_t num;
527         VkSwapchainCreateInfoKHR scinf = {0};
528         VkImageViewCreateInfo ivinf;
529
530         if(vksc_extent.width <= 0 || vksc_extent.height <= 0) {
531                 return -1;
532         }
533
534         scinf.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
535         scinf.surface = vksurf;
536         scinf.minImageCount = 2;
537         scinf.imageFormat = vksurf_fmt[vksurf_selfmt].format;
538         scinf.imageColorSpace = vksurf_fmt[vksurf_selfmt].colorSpace;
539         scinf.imageExtent = vksc_extent;
540         scinf.imageArrayLayers = 1;
541         scinf.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
542         scinf.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
543         scinf.preTransform = vksurf_caps.currentTransform;
544         scinf.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
545         scinf.presentMode = VK_PRESENT_MODE_FIFO_KHR;
546         scinf.clipped = VK_TRUE;
547
548         if(vkCreateSwapchainKHR(vkdev, &scinf, 0, &vksc) != 0) {
549                 fprintf(stderr, "failed to create swapchain\n");
550                 return -1;
551         }
552
553         if(!vksc_img || vksc_numimg != num) {
554                 free(vksc_img);
555                 vkGetSwapchainImagesKHR(vkdev, vksc, &num, 0);
556                 vksc_img = malloc_nf(num * sizeof *vksc_img);
557                 vkGetSwapchainImagesKHR(vkdev, vksc, &num, vksc_img);
558         }
559         if(!vksc_view || vksc_numimg != num) {
560                 free(vksc_view);
561                 vksc_view = malloc_nf(num * sizeof *vksc_view);
562         }
563         vksc_numimg = num;
564
565         for(i=0; i<num; i++) {
566                 memset(&ivinf, 0, sizeof ivinf);
567                 ivinf.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
568                 ivinf.image = vksc_img[i];
569                 ivinf.format = vksurf_fmt[vksurf_selfmt].format;
570                 ivinf.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
571                 ivinf.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
572                 ivinf.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
573                 ivinf.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
574                 ivinf.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
575                 ivinf.subresourceRange.levelCount = 1;
576                 ivinf.subresourceRange.layerCount = 1;
577                 ivinf.viewType = VK_IMAGE_VIEW_TYPE_2D;
578
579                 if(vkCreateImageView(vkdev, &ivinf, 0, vksc_view + i) != 0) {
580                         fprintf(stderr, "failed to create image view (%d)\n", i);
581                         return -1;
582                 }
583         }
584         return 0;
585 }
586
587
588 static int eval_pdev_score(VkPhysicalDevice dev)
589 {
590         int score = 0;
591         uint32_t i, num_fmt, num_qfam, num_ext;
592         VkQueueFamilyProperties *qfam;
593         VkExtensionProperties *ext;
594         VkPhysicalDeviceProperties prop;
595         VkPhysicalDeviceFeatures feat;
596         VkSurfaceFormatKHR *sfmt;
597         VkBool32 can_pres;
598
599         vkGetPhysicalDeviceProperties(dev, &prop);
600         vkGetPhysicalDeviceFeatures(dev, &feat);
601
602         /* check if we have the swapchain extension */
603         vkEnumerateDeviceExtensionProperties(dev, 0, &num_ext, 0);
604         ext = malloc_nf(num_ext * sizeof *ext);
605         vkEnumerateDeviceExtensionProperties(dev, 0, &num_ext, ext);
606
607         if(!have_ext(ext, num_ext, "VK_KHR_swapchain")) {
608                 free(ext);
609                 return 0;
610         }
611
612         /* populate format and present modes arrays, and make sure we have some of each */
613         vkGetPhysicalDeviceSurfaceFormatsKHR(dev, vksurf, &num_fmt, 0);
614         if(!num_fmt) {
615                 free(ext);
616                 return 0;
617         }
618         sfmt = malloc_nf(num_fmt * sizeof *sfmt);
619         vkGetPhysicalDeviceSurfaceFormatsKHR(dev, vksurf, &num_fmt, sfmt);
620
621         vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, vksurf, &vksurf_caps);
622
623         /* find a queue family which can do graphics and can present */
624         vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, 0);
625         qfam = malloc_nf(num_qfam * sizeof *qfam);
626         vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, qfam);
627
628         for(i=0; i<num_qfam; i++) {
629                 vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, vksurf, &can_pres);
630                 if(qfam[i].queueCount && (qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && can_pres) {
631                         score = 1;
632                 }
633         }
634
635         switch(prop.deviceType) {
636         case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
637                 score++;
638                 break;
639         case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
640                 score += 2;
641                 break;
642         case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
643                 score += 4;
644                 break;
645         default:
646                 break;
647         }
648
649         if(initflags & VKINIT_RAY) {
650                 if(have_ext(ext, num_ext, "VK_KHR_acceleration_structure") &&
651                                 have_ext(ext, num_ext, "VK_KHR_ray_tracing_pipeline")) {
652                         score += 100;
653                 }
654         }
655
656         free(ext);
657         free(sfmt);
658         free(qfam);
659         return score;
660 }
661
662 static int choose_pixfmt(void)
663 {
664         static const VkFormat pref[] = {
665                 VK_FORMAT_B8G8R8_UNORM,
666                 VK_FORMAT_R8G8B8_UNORM,
667                 VK_FORMAT_B8G8R8A8_UNORM,
668                 VK_FORMAT_R8G8B8A8_UNORM
669         };
670         int i, j;
671         uint32_t num_fmt;
672
673         vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, 0);
674         if(!num_fmt) return -1;
675         vksurf_fmt = malloc_nf(num_fmt * sizeof *vksurf_fmt);
676         vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, vksurf_fmt);
677
678         vksurf_selfmt = 0;
679         for(i=0; i<num_fmt; i++) {
680                 if(vksurf_fmt[i].colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
681                         continue;
682                 }
683                 for(j=0; j<sizeof pref / sizeof *pref; j++) {
684                         if(vksurf_fmt[i].format == pref[j]) {
685                                 vksurf_selfmt = i;
686                                 vksurf_numfmt = num_fmt;
687                                 return i;
688                         }
689                 }
690         }
691         free(vksurf_fmt);
692         vksurf_fmt = 0;
693         return -1;
694 }
695
696
697 static int have_inst_layer(const char *name)
698 {
699         int i;
700         for(i=0; i<inst_layers_count; i++) {
701                 if(strcmp(inst_layers[i].layerName, name) == 0) {
702                         return 1;
703                 }
704         }
705         return 0;
706 }
707
708 static int have_ext(VkExtensionProperties *ext, int next, const char *name)
709 {
710         int i;
711         for(i=0; i<next; i++) {
712                 if(strcmp(ext[i].extensionName, name) == 0) {
713                         return 1;
714                 }
715         }
716         return 0;
717 }