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