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