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