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