ebc31ae0bda63298cf542ca49aa3327843f348ab
[vktest3] / src / vk.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdint.h>
4 #include <stdarg.h>
5 #include <errno.h>
6 #include <assert.h>
7 #include <vulkan/vulkan.h>
8 #include "vk.h"
9 #include "util.h"
10 #include "darray.h"
11
12 #ifdef __WIN32__
13 #include <vulkan/vulkan_win32.h>
14 #else
15 /*#include <vulkan/vulkan_xlib.h>*/
16 #include <X11/Xlib-xcb.h>
17 #include <vulkan/vulkan_xcb.h>
18 #endif
19
20 struct rpass {
21         int used;
22         int fmt;
23         int zfmt;
24         int num_colbuf;
25         int num_samples;
26         int clear;
27
28         int vkobj_valid;
29         VkRenderPass vkobj;
30 };
31
32 #define MAX_FB_IMGV     8
33 struct framebuf {
34         int used;
35         int width, height;
36
37         /* if rpasses[rpidx].vkobj != vkrpass, the framebuf is invalid */
38         int rpidx;
39         VkRenderPass vkrpass;
40
41         VkImageView imgv[MAX_FB_IMGV];
42         int num_imgv;
43
44         int vkobj_valid;
45         VkFramebuffer vkobj;
46 };
47
48 struct pipeline {
49         int used;
50         VkViewport vport;
51         VkRect2D scissor;
52         VkShaderModule sdr[VKSDR_MAX];
53         VkPrimitiveTopology prim;
54         VkPolygonMode polymode;
55         float line_width;
56         VkCullModeFlags cull;
57         VkFrontFace     frontface;
58         VkColorComponentFlags colorwr;
59         int zbuf, depthwr;
60         VkCompareOp zop;
61         int stencil, stencilwr;
62         VkStencilOp sfail, szfail, szpass;
63         VkCompareOp sop;
64         unsigned int sref, smask;
65         int blend;
66         VkBlendFactor srcblend, dstblend, srcblend_a, dstblend_a;
67
68         VkRenderPass rpass;
69
70         int vkobj_valid;
71         VkPipeline vkobj;
72         VkPipelineLayout vkobj_layout;  /* TODO probably best to split this */
73 };
74
75
76 static struct rpass *rpasses;
77 static struct framebuf *framebufs;
78 static struct pipeline *pipelines;
79
80
81 static int create_instance(void);
82 static int create_surface(void);
83 static int choose_phys_dev(void);
84 static int create_device(void);
85 static int create_swapchain(void);
86 static int create_default_cmdbuf(void);
87
88 static int choose_pixfmt(void);
89 static int eval_pdev_score(VkPhysicalDevice dev);
90 static int have_inst_layer(const char *name);
91 static int have_ext(VkExtensionProperties *ext, int next, const char *name);
92
93 static Display *dpy;
94 static Window win;
95 static int initflags;
96 #define MAX_INIT_QUEUE  32
97 static struct {
98         unsigned int flags;
99         int count;
100         int qfam;
101         VkCommandPool cmdpool;
102 } initq[MAX_INIT_QUEUE];
103 static int num_initq;
104
105 static VkInstance vk;
106 static VkPhysicalDevice vkpdev;
107 static VkQueueFamilyProperties *qfam;
108 static uint32_t num_qfam;
109 static VkDevice vkdev;
110 static VkSurfaceKHR vksurf;
111 static VkSurfaceCapabilitiesKHR vksurf_caps;
112 static int vksurf_numfmt, vksurf_selfmt;
113 static VkSurfaceFormatKHR *vksurf_fmt;
114 static VkSwapchainKHR vksc;
115 static int vksc_numimg;
116 static VkImage *vksc_img;
117 static VkExtent2D vksc_extent;
118 static VkImageView *vksc_view;
119 static VkCommandBuffer *vksc_cmdbuf;    /* default command buffers (vksc_numimg) */
120
121 static VkLayerProperties *inst_layers;
122 static VkExtensionProperties *inst_ext, *dev_ext;
123 static uint32_t inst_ext_count, dev_ext_count, inst_layers_count;
124
125 static VkPhysicalDevice *pdev_list;
126 static uint32_t num_pdev;
127
128 static int have_raytrace, have_debug_report;
129
130 void vk_init_xwin(Display *d, Window w)
131 {
132         dpy = d;
133         win = w;
134 }
135
136 void vk_init_queue(unsigned int qflags, int count)
137 {
138         int i;
139
140         for(i=0; i<num_initq; i++) {
141                 if(initq[i].flags == qflags) {
142                         initq[i].count += count;
143                         return;
144                 }
145         }
146
147         if(num_initq >= MAX_INIT_QUEUE) {
148                 fprintf(stderr, "vk_init_queue: too many queues\n");
149                 return;
150         }
151         initq[num_initq].flags = qflags;
152         initq[num_initq].count = count;
153         num_initq++;
154 }
155
156 int vk_init(unsigned int flags, unsigned int *usedflags)
157 {
158         if(!num_initq) {
159                 vk_init_queue(VKQ_GFX | VKQ_PRESENT, 1);
160         }
161
162         initflags = flags;
163         if(create_instance() == -1)     return -1;
164         if(create_surface() == -1) return -1;
165         if(choose_phys_dev() == -1) return -1;
166         if(create_device() == -1) return -1;
167
168         if(initflags != flags) {
169                 if(usedflags) {
170                         *usedflags = initflags;
171                 } else {
172                         vk_cleanup();
173                         return -1;
174                 }
175         }
176         return 0;
177 }
178
179 void vk_cleanup(void)
180 {
181         int i;
182
183         free(vksc_img);
184         vksc_img = 0;
185         free(vksc_view);
186         vksc_view = 0;
187         if(vksc_view) {
188                 for(i=0; i<vksc_numimg; i++) {
189                         vkDestroyImageView(vkdev, vksc_view[i], 0);
190                 }
191                 vksc_view = 0;
192         }
193         if(vksc) {
194                 vkDestroySwapchainKHR(vkdev, vksc, 0);
195                 vksc = 0;
196         }
197         if(vkdev) {
198                 vkDestroyDevice(vkdev, 0);
199                 vkdev = 0;
200         }
201         if(vksurf) {
202                 vkDestroySurfaceKHR(vk, vksurf, 0);
203                 vksurf = 0;
204         }
205         if(vk) {
206                 vkDestroyInstance(vk, 0);
207                 vk = 0;
208         }
209         free(inst_layers);
210         inst_layers = 0;
211         free(inst_ext);
212         inst_ext = 0;
213         free(dev_ext);
214         dev_ext = 0;
215         free(pdev_list);
216         pdev_list = 0;
217 }
218
219 int vk_reshape(int xsz, int ysz)
220 {
221         int i;
222
223         if(vksc && vksc_extent.width == xsz && vksc_extent.height == ysz) {
224                 return 0;
225         }
226
227         if(vksc_view) {
228                 for(i=0; i<vksc_numimg; i++) {
229                         vkDestroyImageView(vkdev, vksc_view[i], 0);
230                 }
231         }
232         if(vksc) vkDestroySwapchainKHR(vkdev, vksc, 0);
233
234         vksc_extent.width = xsz;
235         vksc_extent.height = ysz;
236
237         if(create_swapchain() == -1) return -1;
238
239         if(!vksc_cmdbuf) {
240                 if(create_default_cmdbuf() == -1) return -1;
241         }
242
243         /* TODO create depth/stencil buffers as needed (initflags) */
244         return 0;
245 }
246
247 int vk_next_image(VkSemaphore sem)
248 {
249         uint32_t idx;
250         if(vkAcquireNextImageKHR(vkdev, vksc, UINT64_MAX, sem, 0, &idx) != 0) {
251                 return -1;
252         }
253         return (int)idx;
254 }
255
256 int vk_find_qfamily(unsigned int flags)
257 {
258         int i, famidx = -1;
259         VkBool32 can_pres;
260
261         if(!qfam) return -1;    /* not initialized I guess... */
262
263         for(i=0; i<num_qfam; i++) {
264                 vkGetPhysicalDeviceSurfaceSupportKHR(vkpdev, i, vksurf, &can_pres);
265
266                 if((flags & VKQ_PRESENT) && !can_pres) {
267                         continue;
268                 }
269                 if((flags & VKQ_GFX) && !(qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) {
270                         continue;
271                 }
272                 if((flags & VKQ_COMPUTE) && !(qfam[i].queueFlags & VK_QUEUE_COMPUTE_BIT)) {
273                         continue;
274                 }
275
276                 return i;       /* found a suitabe queue family */
277         }
278
279         return -1;
280 }
281
282 VkQueue vk_getq_fam(int fam, int n)
283 {
284         VkQueue q;
285
286         if(fam < 0) return 0;
287         if(n < 0 || n >= qfam[fam].queueCount) {
288                 fprintf(stderr, "vk_getq_fam: invalid index %d, family %d has %d queues\n",
289                            n, fam, qfam[fam].queueCount);
290                 return 0;
291         }
292
293         vkGetDeviceQueue(vkdev, fam, n, &q);
294         return q;
295 }
296
297 VkQueue vk_getq(unsigned int flags, int n)
298 {
299         return vk_getq_fam(vk_find_qfamily(flags), n);
300 }
301
302 static VkCommandPool find_cmdpool(int qfam)
303 {
304         int i;
305         VkCommandPoolCreateInfo pinf;
306
307         for(i=0; i<num_initq; i++) {
308                 if(initq[i].qfam == qfam) {
309                         if(!initq[i].cmdpool) {
310                                 /* allocate command pool for this queue family */
311                                 memset(&pinf, 0, sizeof pinf);
312                                 pinf.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
313                                 pinf.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
314                                 pinf.queueFamilyIndex = qfam;
315
316                                 if(vkCreateCommandPool(vkdev, &pinf, 0, &initq[i].cmdpool) != 0) {
317                                         fprintf(stderr, "ck_create_cmdbuf: failed to create command buffer pool\n");
318                                         return 0;
319                                 }
320                         }
321                         return initq[i].cmdpool;
322                 }
323         }
324
325         fprintf(stderr, "vk_create_cmdbuf: failed to find command pool for queue family: %d\n", qfam);
326         return 0;
327 }
328
329 VkCommandBuffer vk_create_cmdbuf_fam(int qfam, int level)
330 {
331         VkCommandBufferAllocateInfo inf = {0};
332         VkCommandBuffer cmdbuf;
333         VkCommandPool cmdpool;
334
335         if(!(cmdpool = find_cmdpool(qfam))) {
336                 return 0;
337         }
338
339         inf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
340         inf.commandPool = cmdpool;
341         inf.level = level;
342         inf.commandBufferCount = 1;
343
344         if(vkAllocateCommandBuffers(vkdev, &inf, &cmdbuf) != 0) {
345                 fprintf(stderr, "vk_create_cmdbuf: failed to allocate command buffer\n");
346                 return 0;
347         }
348         return cmdbuf;
349 }
350
351 VkCommandBuffer vk_create_cmdbuf(unsigned int qflags, int level)
352 {
353         int qfam;
354
355         if((qfam = vk_find_qfamily(qflags)) == -1) {
356                 fprintf(stderr, "vk_create_cmdbuf: failed to find matching queue family\n");
357                 return 0;
358         }
359         return vk_create_cmdbuf_fam(qfam, level);
360 }
361
362 int vk_create_rpass(void)
363 {
364         int i;
365         struct rpass rpass = {0}, *rp = &rpass;
366
367         if(!rpasses) {
368                 rpasses = darr_alloc(0, sizeof *rpasses);
369                 darr_push(rpasses, &rpass);     /* add dummy rpass */
370         }
371
372         for(i=1; i<darr_size(rpasses); i++) {
373                 if(!rpasses[i].used) {
374                         rp = rpasses + i;
375                 }
376         }
377
378         /* init renderpass defaults */
379         rp->used = 1;
380         rp->fmt = vksurf_fmt[vksurf_selfmt].format;
381         rp->zfmt = VK_FORMAT_D24_UNORM_S8_UINT;
382         rp->num_colbuf = 1;
383         rp->num_samples = 1;
384         rp->clear = 1;
385         rp->vkobj_valid = 0;
386         rp->vkobj = 0;
387
388         if(rp == &rpass) {
389                 darr_push(rpasses, rp);
390                 return darr_size(rpasses) - 1;
391         }
392         return rp - rpasses;
393 }
394
395 void vk_free_rpass(int rp)
396 {
397         if(!rpasses || rp < 1 || rp >= darr_size(rpasses)) {
398                 return;
399         }
400
401         if(rpasses[rp].used && rpasses[rp].vkobj) {
402                 vkDestroyRenderPass(vkdev, rpasses[rp].vkobj, 0);
403         }
404         rpasses[rp].used = 0;
405 }
406
407 void vk_rpass_colorbuf(int rp, int fmt, int n)
408 {
409         rpasses[rp].fmt = fmt;
410         rpasses[rp].num_colbuf = n;
411         rpasses[rp].vkobj_valid = 0;
412 }
413
414 void vk_rpass_msaa(int rp, int nsamp)
415 {
416         rpasses[rp].num_samples = nsamp;
417         rpasses[rp].vkobj_valid = 0;
418 }
419
420 void vk_rpass_clear(int rp, int clear)
421 {
422         rpasses[rp].clear = clear;
423         rpasses[rp].vkobj_valid = 0;
424 }
425
426 VkRenderPass vk_rpass(int rp)
427 {
428         int i, zidx;
429         struct rpass *r;
430         VkAttachmentDescription att[17];
431         VkAttachmentReference catref[16], zatref;
432         VkSubpassDescription subpass;
433         VkRenderPassCreateInfo pinf;
434
435         r = rpasses + rp;
436
437         if(!r->vkobj_valid) {
438                 if(r->vkobj) {
439                         vkDestroyRenderPass(vkdev, r->vkobj, 0);
440                         r->vkobj = 0;
441                 }
442
443                 zidx = r->num_colbuf;
444                 memset(att, 0, sizeof att);
445                 for(i=0; i<r->num_colbuf; i++) {
446                         att[i].format = r->fmt;
447                         att[i].samples = r->num_samples;
448                         att[i].loadOp = r->clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
449                         att[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
450                         att[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
451                         att[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
452                         att[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
453                         att[i].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
454                 }
455                 att[zidx].format = r->zfmt;
456                 att[zidx].samples = 1;
457                 att[zidx].loadOp = r->clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
458                 att[zidx].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
459                 att[zidx].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
460                 att[zidx].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
461                 att[zidx].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
462                 att[zidx].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
463
464                 for(i=0; i<r->num_colbuf; i++) {
465                         catref[i].attachment = i;
466                         catref[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
467                 }
468                 zatref.attachment = zidx;
469                 zatref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
470
471                 memset(&subpass, 0, sizeof subpass);
472                 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
473                 subpass.colorAttachmentCount = r->num_colbuf;
474                 subpass.pColorAttachments = catref;
475                 subpass.pDepthStencilAttachment = &zatref;
476
477                 memset(&pinf, 0, sizeof pinf);
478                 pinf.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
479                 pinf.attachmentCount = r->num_colbuf + 1;
480                 pinf.pAttachments = att;
481                 pinf.subpassCount = 1;
482                 pinf.pSubpasses = &subpass;
483
484                 if(vkCreateRenderPass(vkdev, &pinf, 0, &r->vkobj) != 0) {
485                         fprintf(stderr, "failed to create render pass!\n");
486                         return 0;
487                 }
488                 r->vkobj_valid = 1;
489         }
490
491         return r->vkobj;
492 }
493
494
495 int vk_create_fb(void)
496 {
497         int i;
498         struct framebuf framebuf = {0}, *fb = &framebuf;
499
500         if(!framebufs) {
501                 framebufs = darr_alloc(0, sizeof *framebufs);
502                 darr_push(framebufs, &framebuf);        /* add dummy rpass */
503         }
504
505         for(i=1; i<darr_size(framebufs); i++) {
506                 if(!framebufs[i].used) {
507                         fb = framebufs + i;
508                 }
509         }
510
511         /* init framebuffer defaults */
512         memset(fb, 0, sizeof &fb);
513         fb->used = 1;
514
515         if(fb == &framebuf) {
516                 darr_push(framebufs, fb);
517                 return darr_size(framebufs) - 1;
518         }
519         return fb - framebufs;
520 }
521
522 void vk_free_fb(int fb)
523 {
524         if(!framebufs || fb < 1 || fb >= darr_size(framebufs)) {
525                 return;
526         }
527
528         if(framebufs[fb].used && framebufs[fb].vkobj) {
529                 vkDestroyFramebuffer(vkdev, framebufs[fb].vkobj, 0);
530         }
531         framebufs[fb].used = 0;
532 }
533
534 void vk_fb_size(int fb, int x, int  y)
535 {
536         framebufs[fb].width = x;
537         framebufs[fb].height = y;
538         framebufs[fb].vkobj_valid = 0;
539 }
540
541 void vk_fb_rpass(int fb, int rpass)
542 {
543         if(rpass < 0 || rpass >= darr_size(rpasses) || !rpasses[rpass].used) {
544                 fprintf(stderr, "vk_fb_rpass: %d is not a valid renderpass\n", rpass);
545                 return;
546         }
547
548         framebufs[fb].rpidx = rpass;
549         if(rpasses[rpass].vkobj_valid) {
550                 framebufs[fb].vkrpass = rpasses[rpass].vkobj;
551         } else {
552                 framebufs[fb].vkrpass = 0;
553         }
554         framebufs[fb].vkobj_valid = 0;
555 }
556
557 void vk_fb_images(int fb, int n, ...)
558 {
559         int i;
560         va_list ap;
561
562         if(n > MAX_FB_IMGV) {
563                 fprintf(stderr, "vk_fb_images: %d is too many images\n", n);
564                 n = MAX_FB_IMGV;
565         }
566
567         va_start(ap, n);
568         for(i=0; i<n; i++) {
569                 framebufs[fb].imgv[i] = va_arg(ap, VkImageView);
570         }
571         va_end(ap);
572         framebufs[fb].num_imgv = n;
573         framebufs[fb].vkobj_valid = 0;
574 }
575
576 VkFramebuffer vk_fb(int fb)
577 {
578         VkFramebufferCreateInfo fbinf;
579         VkRenderPass rpass;
580         struct framebuf *f;
581
582         f = framebufs + fb;
583
584         if(!(rpass = vk_rpass(f->rpidx))) {
585                 return 0;
586         }
587
588         if(rpass != f->vkrpass || !f->vkobj_valid) {
589                 f->vkrpass = rpass;
590                 if(f->vkobj) {
591                         vkDestroyFramebuffer(vkdev, f->vkobj, 0);
592                         f->vkobj = 0;
593                 }
594
595                 memset(&fbinf, 0, sizeof fbinf);
596                 fbinf.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
597                 fbinf.renderPass = rpass;
598                 fbinf.attachmentCount = f->num_imgv;
599                 fbinf.pAttachments = f->imgv;
600                 fbinf.width = f->width;
601                 fbinf.height = f->height;
602
603                 if(vkCreateFramebuffer(vkdev, &fbinf, 0, &f->vkobj) != 0) {
604                         fprintf(stderr, "vk_fb: failed to create framebuffer\n");
605                         return 0;
606                 }
607                 f->vkobj_valid = 1;
608         }
609         return f->vkobj;
610 }
611
612
613 int vk_create_pipeln(void)
614 {
615         int i;
616         struct pipeline pipeln = {0}, *pp = &pipeln;
617
618         if(!pipelines) {
619                 pipelines = darr_alloc(0, sizeof *pipelines);
620                 darr_push(pipelines, &pipeln);  /* add dummy pipeline */
621         }
622
623         for(i=1; i<darr_size(pipelines); i++) {
624                 if(!pipelines[i].used) {
625                         pp = pipelines + i;
626                 }
627         }
628
629         /* init pipeline defaults */
630         memset(pp, 0, sizeof *pp);
631         pp->used = 1;
632         pp->vport.width = pp->scissor.extent.width = 640;
633         pp->vport.height = pp->scissor.extent.height = 480;
634         pp->vport.minDepth = 0.0f;
635         pp->vport.maxDepth = 1.0f;
636         pp->prim = VKPRIM_TRIANGLES;
637         pp->polymode = VK_POLYGON_MODE_FILL;
638         pp->cull = VK_CULL_MODE_BACK_BIT;
639         pp->frontface = VK_FRONT_FACE_COUNTER_CLOCKWISE;
640         pp->line_width = 1.0f;
641         pp->colorwr = 0xf;      /* RGBA */
642         pp->zbuf = 1;
643         pp->zop = VK_COMPARE_OP_LESS;
644         pp->depthwr = 1;
645         pp->stencil = 0;
646         pp->stencilwr = 1;
647         pp->sop = VK_COMPARE_OP_ALWAYS;
648         pp->smask = 0xffffffff;
649         pp->blend = 0;
650         pp->srcblend = pp->srcblend_a = VK_BLEND_FACTOR_ONE;
651         pp->dstblend = pp->dstblend_a = VK_BLEND_FACTOR_ZERO;
652
653         if(pp == &pipeln) {
654                 darr_push(pipelines, pp);
655                 return darr_size(pipelines) - 1;
656         }
657         return pp - pipelines;
658 }
659
660 void vk_free_pipeln(int pp)
661 {
662         if(!pipelines || pp < 1 || pp >= darr_size(pipelines)) {
663                 return;
664         }
665
666         if(pipelines[pp].used && pipelines[pp].vkobj) {
667                 vkDestroyPipeline(vkdev, pipelines[pp].vkobj, 0);
668         }
669         pipelines[pp].used = 0;
670 }
671
672 void vk_pipeln_rpass(int pp, VkRenderPass rp)
673 {
674         struct pipeline *p = pipelines + pp;
675         p->rpass = rp;
676         p->vkobj_valid = 0;
677 }
678
679 void vk_pipeln_viewport(int pp, int x, int y, int width, int height)
680 {
681         struct pipeline *p = pipelines + pp;
682         p->vport.x = x;
683         p->vport.y = y;
684         p->vport.width = width;
685         p->vport.height = height;
686         p->vkobj_valid = 0;
687 }
688
689 void vk_pipeln_scissor(int pp, int x, int y, int width, int height)
690 {
691         struct pipeline *p = pipelines + pp;
692         p->scissor.offset.x = x;
693         p->scissor.offset.y = y;
694         p->scissor.extent.width = width;
695         p->scissor.extent.height = height;
696         p->vkobj_valid = 0;
697 }
698
699 void vk_pipeln_shader(int pp, int type, VkShaderModule sdr)
700 {
701         struct pipeline *p = pipelines + pp;
702         p->sdr[type] = sdr;
703         p->vkobj_valid = 0;
704 }
705
706 /* TODO: vertex input */
707 void vk_pipeln_prim(int pp, int prim)
708 {
709         struct pipeline *p = pipelines + pp;
710         p->prim = prim;
711         p->vkobj_valid = 0;
712 }
713
714 void vk_pipeln_polymode(int pp, int mode)
715 {
716         struct pipeline *p = pipelines + pp;
717         p->polymode = mode;
718         p->vkobj_valid = 0;
719 }
720
721 void vk_pipeln_cull(int pp, int cull)
722 {
723         struct pipeline *p = pipelines + pp;
724         p->cull = cull;
725         p->vkobj_valid = 0;
726 }
727
728 void vk_pipeln_frontface(int pp, int ff)
729 {
730         struct pipeline *p = pipelines + pp;
731         p->frontface = ff;
732         p->vkobj_valid = 0;
733 }
734
735 void vk_pipeln_linewidth(int pp, int w)
736 {
737         struct pipeline *p = pipelines + pp;
738         p->line_width = w;
739         p->vkobj_valid = 0;
740 }
741
742 void vk_pipeln_multisample(int pp, int nsamples)
743 {
744         /* TODO */
745 }
746
747 void vk_pipeln_colormask(int pp, int r, int g, int b, int a)
748 {
749         struct pipeline *p = pipelines + pp;
750         p->colorwr = 0;
751         if(r) p->colorwr |= VK_COLOR_COMPONENT_R_BIT;
752         if(g) p->colorwr |= VK_COLOR_COMPONENT_G_BIT;
753         if(b) p->colorwr |= VK_COLOR_COMPONENT_B_BIT;
754         if(a) p->colorwr |= VK_COLOR_COMPONENT_A_BIT;
755         p->vkobj_valid = 0;
756 }
757
758 void vk_pipeln_depthmask(int pp, int z)
759 {
760         struct pipeline *p = pipelines + pp;
761         p->depthwr = z;
762         p->vkobj_valid = 0;
763 }
764
765 void vk_pipeln_stencilmask(int pp, int s)
766 {
767         struct pipeline *p = pipelines + pp;
768         p->stencilwr = s;
769         p->vkobj_valid = 0;
770 }
771
772 void vk_pipeln_zbuffer(int pp, int enable)
773 {
774         struct pipeline *p = pipelines + pp;
775         p->zbuf = enable;
776         p->vkobj_valid = 0;
777 }
778
779 void vk_pipeln_zbuffer_op(int pp, int op)
780 {
781         struct pipeline *p = pipelines + pp;
782         p->zop = op;
783         p->vkobj_valid = 0;
784 }
785
786 void vk_pipeln_stencil(int pp, int enable)
787 {
788         struct pipeline *p = pipelines + pp;
789         p->stencil = enable;
790         p->vkobj_valid = 0;
791 }
792
793 void vk_pipeln_stencil_op(int pp, int sfail, int zfail, int zpass)
794 {
795         struct pipeline *p = pipelines + pp;
796         p->sfail = sfail;
797         p->szfail = zfail;
798         p->szpass = zpass;
799         p->vkobj_valid = 0;
800 }
801
802 void vk_pipeln_stencil_func(int pp, int op, unsigned int ref, unsigned int mask)
803 {
804         struct pipeline *p = pipelines + pp;
805         p->sop = op;
806         p->sref = ref;
807         p->smask = mask;
808         p->vkobj_valid = 0;
809 }
810
811 void vk_pipeln_blend(int pp, int enable)
812 {
813         struct pipeline *p = pipelines + pp;
814         p->blend = enable;
815         p->vkobj_valid = 0;
816 }
817
818 void vk_pipeln_blendfunc(int pp, int src, int dst)
819 {
820         struct pipeline *p = pipelines + pp;
821         p->srcblend = src;
822         p->dstblend = dst;
823         p->vkobj_valid = 0;
824 }
825
826 VkPipeline vk_pipeln(int pp)
827 {
828         int i, idx, num_sdr;
829         struct pipeline *p = pipelines + pp;
830         VkPipelineShaderStageCreateInfo ssinf[VKSDR_MAX];
831         VkPipelineVertexInputStateCreateInfo vinp;
832         VkPipelineInputAssemblyStateCreateInfo vasm;
833         VkPipelineViewportStateCreateInfo vp;
834         VkPipelineRasterizationStateCreateInfo rast;
835         VkPipelineMultisampleStateCreateInfo msaa;
836         VkPipelineDepthStencilStateCreateInfo zst;
837         VkPipelineColorBlendAttachmentState bat;
838         VkPipelineColorBlendStateCreateInfo blend;
839         VkPipelineLayoutCreateInfo lay;
840         VkGraphicsPipelineCreateInfo pinf;
841
842
843         if(p->vkobj_valid) {
844                 return p->vkobj;
845         }
846
847         if(p->vkobj) {
848                 vkDestroyPipeline(vkdev, p->vkobj, 0);
849                 p->vkobj = 0;
850         }
851
852         memset(ssinf, 0, sizeof ssinf);
853         idx = 0;
854         for(i=0; i<VKSDR_MAX; i++) {
855                 if(p->sdr[idx]) {
856                         ssinf[idx].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
857                         ssinf[idx].stage = VKSDR_STAGE(i);
858                         ssinf[idx].module = p->sdr[idx];
859                         ssinf[idx].pName = "main";
860                         idx++;
861                 }
862         }
863         num_sdr = idx;
864
865         memset(&vinp, 0, sizeof vinp);
866         vinp.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
867
868         memset(&vasm, 0, sizeof vasm);
869         vasm.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
870         vasm.topology = p->prim;
871
872         memset(&vp, 0, sizeof vp);
873         vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
874         vp.viewportCount = 1;
875         vp.pViewports = &p->vport;
876         vp.scissorCount = 1;
877         vp.pScissors = &p->scissor;
878
879         memset(&rast, 0, sizeof rast);
880         rast.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
881         rast.polygonMode = p->polymode;
882         rast.lineWidth = p->line_width;
883         rast.cullMode = p->cull;
884         rast.frontFace = p->frontface;
885
886         /* TODO */
887         memset(&msaa, 0, sizeof msaa);
888         msaa.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
889         msaa.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
890         msaa.minSampleShading = 1.0f;
891
892         memset(&zst, 0, sizeof zst);
893         zst.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
894         zst.depthTestEnable = p->zbuf;
895         zst.depthWriteEnable = p->depthwr;
896         zst.depthCompareOp = p->zop;
897         zst.stencilTestEnable = p->stencil;
898         zst.front.writeMask = p->stencilwr;
899         zst.front.failOp = p->sfail;
900         zst.front.passOp = p->szpass;
901         zst.front.depthFailOp = p->szfail;
902         zst.front.compareOp = p->sop;
903         zst.front.compareMask = p->smask;
904         zst.front.reference = p->sref;
905         zst.back = zst.front;
906
907         memset(&bat, 0, sizeof bat);
908         bat.colorWriteMask = p->colorwr;
909         bat.blendEnable = p->blend;
910         bat.srcColorBlendFactor = p->srcblend;
911         bat.dstColorBlendFactor = p->dstblend;
912         bat.colorBlendOp = VK_BLEND_OP_ADD;     /* TODO */
913         bat.srcAlphaBlendFactor = p->srcblend_a;
914         bat.dstAlphaBlendFactor = p->dstblend_a;
915
916         memset(&blend, 0, sizeof blend);
917         blend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
918         blend.attachmentCount = 1;
919         blend.pAttachments = &bat;
920
921         /* TODO */
922         memset(&lay, 0, sizeof lay);
923         lay.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
924         if(vkCreatePipelineLayout(vkdev, &lay, 0, &p->vkobj_layout) != 0) {
925                 return 0;
926         }
927
928         memset(&pinf, 0, sizeof pinf);
929         pinf.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
930         pinf.stageCount = num_sdr;
931         pinf.pStages = ssinf;
932         pinf.pVertexInputState = &vinp;
933         pinf.pInputAssemblyState = &vasm;
934         pinf.pViewportState = &vp;
935         pinf.pRasterizationState = &rast;
936         pinf.pDepthStencilState = &zst;
937         pinf.pMultisampleState = &msaa;
938         pinf.pColorBlendState = &blend;
939         pinf.layout = p->vkobj_layout;
940         pinf.renderPass = p->rpass;
941         pinf.basePipelineIndex = -1;
942
943         if(vkCreateGraphicsPipelines(vkdev, 0, 1, &pinf, 0, &p->vkobj) != 0) {
944                 return 0;
945         }
946         p->vkobj_valid = 1;
947         return p->vkobj;
948 }
949
950
951 VkShaderModule vk_load_shader(const char *fname)
952 {
953         FILE *fp;
954         long sz;
955         void *buf;
956         VkShaderModuleCreateInfo sinf;
957         VkShaderModule sdr;
958
959         if(!(fp = fopen(fname, "rb"))) {
960                 fprintf(stderr, "failed to open shader: %s: %s\n", fname, strerror(errno));
961                 return 0;
962         }
963         fseek(fp, 0, SEEK_END);
964         sz = ftell(fp);
965         fseek(fp, 0, SEEK_SET);
966
967         buf = alloca(sz);
968         if(fread(buf, 1, sz, fp) < sz) {
969                 fprintf(stderr, "unexpected EOF while reading shader: %s\n", fname);
970                 fclose(fp);
971                 return 0;
972         }
973         fclose(fp);
974
975         memset(&sinf, 0, sizeof sinf);
976         sinf.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
977         sinf.codeSize = sz;
978         sinf.pCode = buf;
979
980         if(vkCreateShaderModule(vkdev, &sinf, 0, &sdr) != 0) {
981                 fprintf(stderr, "failed to create shader from %s\n", fname);
982                 return 0;
983         }
984         return sdr;
985 }
986
987 void vk_free_shader(VkShaderModule sdr)
988 {
989         vkDestroyShaderModule(vkdev, sdr, 0);
990 }
991
992 VkSemaphore vk_create_sem(void)
993 {
994         VkSemaphore sem;
995         VkSemaphoreCreateInfo sinf = {0};
996
997         sinf.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
998         if(vkCreateSemaphore(vkdev, &sinf, 0, &sem) != 0) {
999                 return 0;
1000         }
1001         return sem;
1002 }
1003
1004 void vk_free_sem(VkSemaphore sem)
1005 {
1006         vkDestroySemaphore(vkdev, sem, 0);
1007 }
1008
1009 #define ARRSZ(arr)      (sizeof arr / sizeof *arr)
1010 static const char *known_layer_list[] = {
1011         "VK_LAYER_GOOGLE_threading",
1012         "VK_LAYER_LUNARG_parameter_validation",
1013         "VK_LAYER_LUNARG_object_tracker",
1014         "VK_LAYER_LUNARG_image",
1015         "VK_LAYER_LUNARG_core_validation",
1016         "VK_LAYER_LUNARG_swapchain",
1017         "VK_LAYER_GOOGLE_unique_objects"
1018 };
1019
1020 static struct {
1021         const char *name;
1022         int required;
1023 } known_instext_list[] = {
1024         {"VK_KHR_surface", 1},
1025 #ifdef __WIN32__
1026         {"VK_KHR_win32_surface", 1},
1027 #else
1028         /*{"VK_KHR_xlib_surface", 1},*/
1029         {"VK_KHR_xcb_surface", 1},
1030 #endif
1031         {"VK_KHR_debug_report", 0}
1032 };
1033
1034 static struct {
1035         const char *name;
1036         int required;
1037 } known_devext_list[] = {
1038         {"VK_KHR_swapchain", 1},
1039         {"VK_KHR_acceleration_structure", 0},
1040         {"VK_KHR_ray_tracing_pipeline", 0}
1041 };
1042
1043 static int create_instance(void)
1044 {
1045         int i, nlayers = 0, next = 0;
1046         VkInstanceCreateInfo instinf;
1047         VkApplicationInfo appinf;
1048         const char *layers[ARRSZ(known_layer_list)];
1049         const char *ext[ARRSZ(known_instext_list)];
1050         uint32_t apiver;
1051
1052         vkEnumerateInstanceVersion(&apiver);
1053         printf("Vulkan API version: %d.%d.%d\n", (apiver >> 22) & 0x7f,
1054                         (apiver >> 12) & 0x3ff, apiver & 0xfff);
1055
1056         memset(&appinf, 0, sizeof appinf);
1057         appinf.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
1058         appinf.pApplicationName = "vkray";
1059         appinf.pEngineName = "vkray";
1060         appinf.apiVersion = apiver;
1061
1062         vkEnumerateInstanceLayerProperties(&inst_layers_count, 0);
1063         inst_layers = malloc_nf(inst_layers_count * sizeof *inst_layers);
1064         vkEnumerateInstanceLayerProperties(&inst_layers_count, inst_layers);
1065
1066         vkEnumerateInstanceExtensionProperties(0, &inst_ext_count, 0);
1067         inst_ext = malloc_nf(inst_ext_count * sizeof *inst_ext);
1068         vkEnumerateInstanceExtensionProperties(0, &inst_ext_count, inst_ext);
1069
1070         printf("Layers:\n");
1071         for(i=0; i<inst_layers_count; i++) {
1072                 printf(" - %s: %s\n", inst_layers[i].layerName, inst_layers[i].description);
1073         }
1074         printf("Instance extensions:\n");
1075         for(i=0; i<inst_ext_count; i++) {
1076                 printf(" - %s\n", inst_ext[i].extensionName);
1077         }
1078
1079         have_debug_report = have_ext(inst_ext, inst_ext_count, "VK_KHR_debug_report");
1080
1081         for(i=0; i<ARRSZ(known_layer_list); i++) {
1082                 if(have_inst_layer(known_layer_list[i])) {
1083                         layers[nlayers++] = known_layer_list[i];
1084                 }
1085         }
1086         for(i=0; i<ARRSZ(known_instext_list); i++) {
1087                 if(have_ext(inst_ext, inst_ext_count, known_instext_list[i].name)) {
1088                         ext[next++] = known_instext_list[i].name;
1089                 } else if(known_instext_list[i].required) {
1090                         fprintf(stderr, "Vulkan implementation lacks required instance extension: %s\n",
1091                                         known_instext_list[i].name);
1092                         return -1;
1093                 }
1094         }
1095
1096         memset(&instinf, 0, sizeof instinf);
1097         instinf.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
1098         instinf.pApplicationInfo = &appinf;
1099         instinf.enabledLayerCount = nlayers;
1100         instinf.ppEnabledLayerNames = layers;
1101         instinf.enabledExtensionCount = next;
1102         instinf.ppEnabledExtensionNames = ext;
1103         if(vkCreateInstance(&instinf, 0, &vk) != 0) {
1104                 fprintf(stderr, "failed to create vulkan instance\n");
1105                 return -1;
1106         }
1107
1108         return 0;
1109 }
1110
1111 static int create_surface(void)
1112 {
1113         /*
1114         VkXlibSurfaceCreateInfoKHR xinf = {0};
1115         xinf.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
1116         xinf.dpy = dpy;
1117         xinf.window = win;
1118
1119         if(vkCreateXlibSurfaceKHR(vk, &xinf, 0, &vksurf) != 0) {
1120                 fprintf(stderr, "failed to create Xlib window surface\n");
1121                 return -1;
1122         }
1123         */
1124         VkXcbSurfaceCreateInfoKHR xinf = {0};
1125         xinf.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
1126         xinf.connection = XGetXCBConnection(dpy);
1127         xinf.window = (xcb_window_t)win;
1128
1129         if(vkCreateXcbSurfaceKHR(vk, &xinf, 0, &vksurf) != 0) {
1130                 fprintf(stderr, "failed to create XCB window surface\n");
1131                 return -1;
1132         }
1133         return 0;
1134 }
1135
1136 int choose_phys_dev(void)
1137 {
1138         uint32_t i, num_pdev, score, best_score, best_dev;
1139         VkPhysicalDevice *pdev;
1140         VkPhysicalDeviceProperties pdevprop;
1141         VkBool32 can_pres;
1142
1143         vkEnumeratePhysicalDevices(vk, &num_pdev, 0);
1144         if(!num_pdev) {
1145                 fprintf(stderr, "no vulkan devices found\n");
1146                 return -1;
1147         }
1148         pdev = malloc_nf(num_pdev * sizeof *pdev);
1149         vkEnumeratePhysicalDevices(vk, &num_pdev, pdev);
1150
1151         printf("Found %d physical devices\n", num_pdev);
1152
1153         best_score = 0;
1154         best_dev = -1;
1155         for(i=0; i<num_pdev; i++) {
1156                 if((score = eval_pdev_score(pdev[i])) && score > best_score) {
1157                         best_score = score;
1158                         best_dev = i;
1159                 }
1160
1161                 vkGetPhysicalDeviceProperties(pdev[i], &pdevprop);
1162                 printf(" %d: %s (score: %d)\n", i, pdevprop.deviceName, score);
1163         }
1164         if(best_dev == -1) {
1165                 fprintf(stderr, "no suitable vulkan device found\n");
1166                 free(pdev);
1167                 return -1;
1168         }
1169         vkpdev = pdev[best_dev];
1170
1171         if(qfam) free(qfam);
1172
1173         vkGetPhysicalDeviceQueueFamilyProperties(vkpdev, &num_qfam, 0);
1174         qfam = malloc_nf(num_qfam * sizeof *qfam);
1175         vkGetPhysicalDeviceQueueFamilyProperties(vkpdev, &num_qfam, qfam);
1176
1177         free(pdev);
1178         choose_pixfmt();
1179         return 0;
1180 }
1181
1182 static int create_device(void)
1183 {
1184         float *prio;
1185         VkDeviceQueueCreateInfo qinf[MAX_INIT_QUEUE] = {0};
1186         VkPhysicalDeviceFeatures feat = {0};
1187         VkDeviceCreateInfo devinf = {0};
1188         const char *ext[ARRSZ(known_devext_list) + 16];
1189         int i, j, num_ext, qfam, totalq;
1190
1191         vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, 0);
1192         dev_ext = malloc_nf(dev_ext_count * sizeof *dev_ext);
1193         vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, dev_ext);
1194
1195         num_ext = 0;
1196         for(i=0; i<ARRSZ(known_devext_list); i++) {
1197                 if(have_ext(dev_ext, dev_ext_count, known_devext_list[i].name)) {
1198                         ext[num_ext++] = known_devext_list[i].name;
1199                 } else if(known_devext_list[i].required) {
1200                         fprintf(stderr, "Vulkan device lacks required extension: %s\n",
1201                                         known_devext_list[i].name);
1202                         return -1;
1203                 }
1204         }
1205
1206         if(initflags & VKINIT_RAY) {
1207                 if(have_ext(dev_ext, dev_ext_count, "VK_KHR_acceleration_structure") &&
1208                                 have_ext(dev_ext, dev_ext_count, "VK_KHR_ray_tracing_pipeline")) {
1209                         ext[num_ext++] = "VK_KHR_acceleration_structure";
1210                         ext[num_ext++] = "VK_KHR_ray_tracing_pipeline";
1211                 } else {
1212                         initflags &= ~VKINIT_RAY;
1213                 }
1214         }
1215
1216         totalq = 0;
1217         for(i=0; i<num_initq; i++) {
1218                 totalq += initq[i].count;
1219         }
1220         if(totalq > 1024) {
1221                 fprintf(stderr, "create_device: arbitrary limit of total queues exceeded (%d)\n", totalq);
1222                 return -1;
1223         }
1224         prio = alloca(totalq * sizeof *prio);
1225
1226         for(i=0; i<num_initq; i++) {
1227                 if((qfam = vk_find_qfamily(initq[i].flags)) == -1) {
1228                         fprintf(stderr, "create_device: failed to find queue family (flags: 0x%2x)\n",
1229                                         initq[i].flags);
1230                         return -1;
1231                 }
1232                 initq[i].qfam = qfam;
1233                 initq[i].cmdpool = 0;
1234
1235                 qinf[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1236                 qinf[i].queueFamilyIndex = qfam;
1237                 qinf[i].queueCount = initq[i].count;
1238                 qinf[i].pQueuePriorities = prio;
1239                 for(j=0; j<initq[i].count; i++) {
1240                         *prio++ = 1.0f; /* all queue priorities 1 */
1241                 }
1242         }
1243
1244         devinf.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
1245         devinf.pQueueCreateInfos = qinf;
1246         devinf.queueCreateInfoCount = num_initq;
1247         devinf.pEnabledFeatures = &feat;
1248         devinf.enabledExtensionCount = num_ext;
1249         devinf.ppEnabledExtensionNames = ext;
1250
1251         if(vkCreateDevice(vkpdev, &devinf, 0, &vkdev) != 0) {
1252                 fprintf(stderr, "failed to create vulkan device\n");
1253                 return -1;
1254         }
1255         return 0;
1256 }
1257
1258 static int create_swapchain(void)
1259 {
1260         int i;
1261         uint32_t num;
1262         VkSwapchainCreateInfoKHR scinf = {0};
1263         VkImageViewCreateInfo ivinf;
1264
1265         if(vksc_extent.width <= 0 || vksc_extent.height <= 0) {
1266                 return -1;
1267         }
1268
1269         scinf.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1270         scinf.surface = vksurf;
1271         scinf.minImageCount = 2;
1272         scinf.imageFormat = vksurf_fmt[vksurf_selfmt].format;
1273         scinf.imageColorSpace = vksurf_fmt[vksurf_selfmt].colorSpace;
1274         scinf.imageExtent = vksc_extent;
1275         scinf.imageArrayLayers = 1;
1276         scinf.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1277         scinf.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1278         scinf.preTransform = vksurf_caps.currentTransform;
1279         scinf.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1280         scinf.presentMode = VK_PRESENT_MODE_FIFO_KHR;
1281         scinf.clipped = VK_TRUE;
1282
1283         if(vkCreateSwapchainKHR(vkdev, &scinf, 0, &vksc) != 0) {
1284                 fprintf(stderr, "failed to create swapchain\n");
1285                 return -1;
1286         }
1287
1288         if(!vksc_img || vksc_numimg != num) {
1289                 free(vksc_img);
1290                 vkGetSwapchainImagesKHR(vkdev, vksc, &num, 0);
1291                 vksc_img = malloc_nf(num * sizeof *vksc_img);
1292                 vkGetSwapchainImagesKHR(vkdev, vksc, &num, vksc_img);
1293         }
1294         if(!vksc_view || vksc_numimg != num) {
1295                 free(vksc_view);
1296                 vksc_view = malloc_nf(num * sizeof *vksc_view);
1297         }
1298         vksc_numimg = num;
1299
1300         for(i=0; i<num; i++) {
1301                 memset(&ivinf, 0, sizeof ivinf);
1302                 ivinf.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1303                 ivinf.image = vksc_img[i];
1304                 ivinf.format = vksurf_fmt[vksurf_selfmt].format;
1305                 ivinf.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
1306                 ivinf.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
1307                 ivinf.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
1308                 ivinf.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
1309                 ivinf.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1310                 ivinf.subresourceRange.levelCount = 1;
1311                 ivinf.subresourceRange.layerCount = 1;
1312                 ivinf.viewType = VK_IMAGE_VIEW_TYPE_2D;
1313
1314                 if(vkCreateImageView(vkdev, &ivinf, 0, vksc_view + i) != 0) {
1315                         fprintf(stderr, "failed to create image view (%d)\n", i);
1316                         return -1;
1317                 }
1318         }
1319         return 0;
1320 }
1321
1322 static int create_default_cmdbuf(void)
1323 {
1324         int i, qfam;
1325         VkCommandPool cmdpool;
1326         VkCommandBufferAllocateInfo cbinf = {0};
1327
1328         assert(!vksc_cmdbuf);
1329
1330         if((qfam = vk_find_qfamily(VKQ_GFX | VKQ_PRESENT)) == -1) {
1331                 fprintf(stderr, "failed to find a gfx|present capable queue family\n");
1332                 return -1;
1333         }
1334         if(!(cmdpool = find_cmdpool(qfam))) {
1335                 fprintf(stderr, "failed to find usable command pool for default command buffers\n");
1336                 return -1;
1337         }
1338         if(!(vksc_cmdbuf = malloc(vksc_numimg * sizeof *vksc_cmdbuf))) {
1339                 fprintf(stderr, "failed to allocate %d command buffers for the swap-chain\n", vksc_numimg);
1340                 return -1;
1341         }
1342
1343         cbinf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1344         cbinf.commandPool = cmdpool;
1345         cbinf.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1346         cbinf.commandBufferCount = vksc_numimg;
1347
1348         if(vkAllocateCommandBuffers(vkdev, &cbinf, vksc_cmdbuf) != 0) {
1349                 fprintf(stderr, "failed to create %d command buffers for the swap-chain\n", vksc_numimg);
1350                 return -1;
1351         }
1352         return 0;
1353 }
1354
1355 static int eval_pdev_score(VkPhysicalDevice dev)
1356 {
1357         int score = 0;
1358         uint32_t i, num_fmt, num_qfam, num_ext;
1359         VkQueueFamilyProperties *qfam;
1360         VkExtensionProperties *ext;
1361         VkPhysicalDeviceProperties prop;
1362         VkPhysicalDeviceFeatures feat;
1363         VkSurfaceFormatKHR *sfmt;
1364         VkBool32 can_pres;
1365
1366         vkGetPhysicalDeviceProperties(dev, &prop);
1367         vkGetPhysicalDeviceFeatures(dev, &feat);
1368
1369         /* check if we have the swapchain extension */
1370         vkEnumerateDeviceExtensionProperties(dev, 0, &num_ext, 0);
1371         ext = malloc_nf(num_ext * sizeof *ext);
1372         vkEnumerateDeviceExtensionProperties(dev, 0, &num_ext, ext);
1373
1374         if(!have_ext(ext, num_ext, "VK_KHR_swapchain")) {
1375                 free(ext);
1376                 return 0;
1377         }
1378
1379         /* populate format and present modes arrays, and make sure we have some of each */
1380         vkGetPhysicalDeviceSurfaceFormatsKHR(dev, vksurf, &num_fmt, 0);
1381         if(!num_fmt) {
1382                 free(ext);
1383                 return 0;
1384         }
1385         sfmt = malloc_nf(num_fmt * sizeof *sfmt);
1386         vkGetPhysicalDeviceSurfaceFormatsKHR(dev, vksurf, &num_fmt, sfmt);
1387
1388         vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, vksurf, &vksurf_caps);
1389
1390         /* find a queue family which can do graphics and can present */
1391         vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, 0);
1392         qfam = malloc_nf(num_qfam * sizeof *qfam);
1393         vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, qfam);
1394
1395         for(i=0; i<num_qfam; i++) {
1396                 vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, vksurf, &can_pres);
1397                 if(qfam[i].queueCount && (qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && can_pres) {
1398                         score = 1;
1399                 }
1400         }
1401
1402         switch(prop.deviceType) {
1403         case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
1404                 score++;
1405                 break;
1406         case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
1407                 score += 2;
1408                 break;
1409         case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
1410                 score += 4;
1411                 break;
1412         default:
1413                 break;
1414         }
1415
1416         if(initflags & VKINIT_RAY) {
1417                 if(have_ext(ext, num_ext, "VK_KHR_acceleration_structure") &&
1418                                 have_ext(ext, num_ext, "VK_KHR_ray_tracing_pipeline")) {
1419                         score += 100;
1420                 }
1421         }
1422
1423         free(ext);
1424         free(sfmt);
1425         free(qfam);
1426         return score;
1427 }
1428
1429 static int choose_pixfmt(void)
1430 {
1431         static const VkFormat pref[] = {
1432                 VK_FORMAT_B8G8R8_UNORM,
1433                 VK_FORMAT_R8G8B8_UNORM,
1434                 VK_FORMAT_B8G8R8A8_UNORM,
1435                 VK_FORMAT_R8G8B8A8_UNORM
1436         };
1437         int i, j;
1438         uint32_t num_fmt;
1439
1440         vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, 0);
1441         if(!num_fmt) return -1;
1442         vksurf_fmt = malloc_nf(num_fmt * sizeof *vksurf_fmt);
1443         vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, vksurf_fmt);
1444
1445         vksurf_selfmt = 0;
1446         for(i=0; i<num_fmt; i++) {
1447                 if(vksurf_fmt[i].colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
1448                         continue;
1449                 }
1450                 for(j=0; j<sizeof pref / sizeof *pref; j++) {
1451                         if(vksurf_fmt[i].format == pref[j]) {
1452                                 vksurf_selfmt = i;
1453                                 vksurf_numfmt = num_fmt;
1454                                 return i;
1455                         }
1456                 }
1457         }
1458         free(vksurf_fmt);
1459         vksurf_fmt = 0;
1460         return -1;
1461 }
1462
1463
1464 static int have_inst_layer(const char *name)
1465 {
1466         int i;
1467         for(i=0; i<inst_layers_count; i++) {
1468                 if(strcmp(inst_layers[i].layerName, name) == 0) {
1469                         return 1;
1470                 }
1471         }
1472         return 0;
1473 }
1474
1475 static int have_ext(VkExtensionProperties *ext, int next, const char *name)
1476 {
1477         int i;
1478         for(i=0; i<next; i++) {
1479                 if(strcmp(ext[i].extensionName, name) == 0) {
1480                         return 1;
1481                 }
1482         }
1483         return 0;
1484 }