7 #include <vulkan/vulkan.h>
13 #include <vulkan/vulkan_win32.h>
15 /*#include <vulkan/vulkan_xlib.h>*/
16 #include <X11/Xlib-xcb.h>
17 #include <vulkan/vulkan_xcb.h>
37 /* if rpasses[rpidx].vkobj != vkrpass, the framebuf is invalid */
41 VkImageView imgv[MAX_FB_IMGV];
52 VkShaderModule sdr[VKSDR_MAX];
53 VkPrimitiveTopology prim;
54 VkPolygonMode polymode;
57 VkFrontFace frontface;
58 VkColorComponentFlags colorwr;
61 int stencil, stencilwr;
62 VkStencilOp sfail, szfail, szpass;
64 unsigned int sref, smask;
66 VkBlendFactor srcblend, dstblend, srcblend_a, dstblend_a;
72 VkPipelineLayout vkobj_layout; /* TODO probably best to split this */
76 static struct rpass *rpasses;
77 static struct framebuf *framebufs;
78 static struct pipeline *pipelines;
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);
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);
96 #define MAX_INIT_QUEUE 32
101 VkCommandPool cmdpool;
102 } initq[MAX_INIT_QUEUE];
103 static int num_initq;
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) */
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;
125 static VkPhysicalDevice *pdev_list;
126 static uint32_t num_pdev;
128 static int have_raytrace, have_debug_report;
130 void vk_init_xwin(Display *d, Window w)
136 void vk_init_queue(unsigned int qflags, int count)
140 for(i=0; i<num_initq; i++) {
141 if(initq[i].flags == qflags) {
142 initq[i].count += count;
147 if(num_initq >= MAX_INIT_QUEUE) {
148 fprintf(stderr, "vk_init_queue: too many queues\n");
151 initq[num_initq].flags = qflags;
152 initq[num_initq].count = count;
156 int vk_init(unsigned int flags, unsigned int *usedflags)
159 vk_init_queue(VKQ_GFX | VKQ_PRESENT, 1);
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;
168 if(initflags != flags) {
170 *usedflags = initflags;
179 void vk_cleanup(void)
188 for(i=0; i<vksc_numimg; i++) {
189 vkDestroyImageView(vkdev, vksc_view[i], 0);
194 vkDestroySwapchainKHR(vkdev, vksc, 0);
198 vkDestroyDevice(vkdev, 0);
202 vkDestroySurfaceKHR(vk, vksurf, 0);
206 vkDestroyInstance(vk, 0);
219 int vk_reshape(int xsz, int ysz)
223 if(vksc && vksc_extent.width == xsz && vksc_extent.height == ysz) {
228 for(i=0; i<vksc_numimg; i++) {
229 vkDestroyImageView(vkdev, vksc_view[i], 0);
232 if(vksc) vkDestroySwapchainKHR(vkdev, vksc, 0);
234 vksc_extent.width = xsz;
235 vksc_extent.height = ysz;
237 if(create_swapchain() == -1) return -1;
240 if(create_default_cmdbuf() == -1) return -1;
243 /* TODO create depth/stencil buffers as needed (initflags) */
247 int vk_num_swap_images(void)
252 VkImageView vk_swap_image(int idx)
254 return vksc_view[idx];
257 int vk_next_swap_image(VkSemaphore sem)
260 if(vkAcquireNextImageKHR(vkdev, vksc, UINT64_MAX, sem, 0, &idx) != 0) {
266 int vk_submit(VkQueue q, VkCommandBuffer cmdbuf, VkSemaphore semwait, VkSemaphore semsig)
268 /* TODO: investigate if we need to expose the wait stage */
269 VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
270 VkSubmitInfo sinf = {0};
271 sinf.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
272 sinf.waitSemaphoreCount = semwait ? 1 : 0;
273 sinf.pWaitSemaphores = &semwait;
274 sinf.pWaitDstStageMask = &wait_stage;
275 sinf.commandBufferCount = 1;
276 sinf.pCommandBuffers = &cmdbuf;
277 sinf.signalSemaphoreCount = semsig ? 1 : 0;
278 sinf.pSignalSemaphores = &semsig;
280 if(vkQueueSubmit(q, 1, &sinf, 0) != 0) {
281 fprintf(stderr, "failed to submit command buffer\n");
287 int vk_present(VkQueue q, int imgid, VkSemaphore semwait)
289 VkPresentInfoKHR pinf = {0};
291 pinf.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
292 pinf.waitSemaphoreCount = semwait ? 1 : 0;
293 pinf.pWaitSemaphores = &semwait;
294 pinf.swapchainCount = 1;
295 pinf.pSwapchains = &vksc;
296 pinf.pImageIndices = (unsigned int*)&imgid;
298 if(vkQueuePresentKHR(q, &pinf) != 0) {
299 fprintf(stderr, "present failed\n");
305 int vk_find_qfamily(unsigned int flags)
310 if(!qfam) return -1; /* not initialized I guess... */
312 for(i=0; i<num_qfam; i++) {
313 vkGetPhysicalDeviceSurfaceSupportKHR(vkpdev, i, vksurf, &can_pres);
315 if((flags & VKQ_PRESENT) && !can_pres) {
318 if((flags & VKQ_GFX) && !(qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) {
321 if((flags & VKQ_COMPUTE) && !(qfam[i].queueFlags & VK_QUEUE_COMPUTE_BIT)) {
325 return i; /* found a suitabe queue family */
331 VkQueue vk_getq_fam(int fam, int n)
335 if(fam < 0) return 0;
336 if(n < 0 || n >= qfam[fam].queueCount) {
337 fprintf(stderr, "vk_getq_fam: invalid index %d, family %d has %d queues\n",
338 n, fam, qfam[fam].queueCount);
342 vkGetDeviceQueue(vkdev, fam, n, &q);
346 VkQueue vk_getq(unsigned int flags, int n)
348 return vk_getq_fam(vk_find_qfamily(flags), n);
351 static VkCommandPool find_cmdpool(int qfam)
354 VkCommandPoolCreateInfo pinf;
356 for(i=0; i<num_initq; i++) {
357 if(initq[i].qfam == qfam) {
358 if(!initq[i].cmdpool) {
359 /* allocate command pool for this queue family */
360 memset(&pinf, 0, sizeof pinf);
361 pinf.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
362 pinf.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
363 pinf.queueFamilyIndex = qfam;
365 if(vkCreateCommandPool(vkdev, &pinf, 0, &initq[i].cmdpool) != 0) {
366 fprintf(stderr, "ck_create_cmdbuf: failed to create command buffer pool\n");
370 return initq[i].cmdpool;
374 fprintf(stderr, "vk_create_cmdbuf: failed to find command pool for queue family: %d\n", qfam);
378 VkCommandBuffer vk_create_cmdbuf_fam(int qfam, int level)
380 VkCommandBufferAllocateInfo inf = {0};
381 VkCommandBuffer cmdbuf;
382 VkCommandPool cmdpool;
384 if(!(cmdpool = find_cmdpool(qfam))) {
388 inf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
389 inf.commandPool = cmdpool;
391 inf.commandBufferCount = 1;
393 if(vkAllocateCommandBuffers(vkdev, &inf, &cmdbuf) != 0) {
394 fprintf(stderr, "vk_create_cmdbuf: failed to allocate command buffer\n");
400 VkCommandBuffer vk_create_cmdbuf(unsigned int qflags, int level)
404 if((qfam = vk_find_qfamily(qflags)) == -1) {
405 fprintf(stderr, "vk_create_cmdbuf: failed to find matching queue family\n");
408 return vk_create_cmdbuf_fam(qfam, level);
411 VkCommandBuffer vk_get_cmdbuf(int imgid)
413 if(imgid < 0 || imgid >= vksc_numimg) {
414 fprintf(stderr, "vk_get_cmdbuf: invalid id %d, swap chain has %d images\n",
418 return vksc_cmdbuf[imgid];
421 int vk_create_rpass(void)
424 struct rpass rpass = {0}, *rp = &rpass;
427 rpasses = darr_alloc(0, sizeof *rpasses);
428 darr_push(rpasses, &rpass); /* add dummy rpass */
431 for(i=1; i<darr_size(rpasses); i++) {
432 if(!rpasses[i].used) {
437 /* init renderpass defaults */
439 rp->fmt = vksurf_fmt[vksurf_selfmt].format;
440 rp->zfmt = VK_FORMAT_D24_UNORM_S8_UINT;
448 darr_push(rpasses, rp);
449 return darr_size(rpasses) - 1;
454 void vk_free_rpass(int rp)
456 if(!rpasses || rp < 1 || rp >= darr_size(rpasses)) {
460 if(rpasses[rp].used && rpasses[rp].vkobj) {
461 vkDestroyRenderPass(vkdev, rpasses[rp].vkobj, 0);
463 rpasses[rp].used = 0;
466 void vk_rpass_colorbuf(int rp, int fmt, int n)
468 rpasses[rp].fmt = fmt;
469 rpasses[rp].num_colbuf = n;
470 rpasses[rp].vkobj_valid = 0;
473 void vk_rpass_msaa(int rp, int nsamp)
475 rpasses[rp].num_samples = nsamp;
476 rpasses[rp].vkobj_valid = 0;
479 void vk_rpass_clear(int rp, int clear)
481 rpasses[rp].clear = clear;
482 rpasses[rp].vkobj_valid = 0;
485 VkRenderPass vk_rpass(int rp)
489 VkAttachmentDescription att[17];
490 VkAttachmentReference catref[16], zatref;
491 VkSubpassDescription subpass;
492 VkRenderPassCreateInfo pinf;
496 if(!r->vkobj_valid) {
498 vkDestroyRenderPass(vkdev, r->vkobj, 0);
502 zidx = r->num_colbuf;
503 memset(att, 0, sizeof att);
504 for(i=0; i<r->num_colbuf; i++) {
505 att[i].format = r->fmt;
506 att[i].samples = r->num_samples;
507 att[i].loadOp = r->clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
508 att[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
509 att[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
510 att[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
511 att[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
512 att[i].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
515 att[zidx].format = r->zfmt;
516 att[zidx].samples = 1;
517 att[zidx].loadOp = r->clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
518 att[zidx].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
519 att[zidx].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
520 att[zidx].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
521 att[zidx].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
522 att[zidx].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
525 for(i=0; i<r->num_colbuf; i++) {
526 catref[i].attachment = i;
527 catref[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
530 zatref.attachment = zidx;
531 zatref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
534 memset(&subpass, 0, sizeof subpass);
535 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
536 subpass.colorAttachmentCount = r->num_colbuf;
537 subpass.pColorAttachments = catref;
538 subpass.pDepthStencilAttachment = 0;//&zatref;
540 memset(&pinf, 0, sizeof pinf);
541 pinf.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
542 pinf.attachmentCount = r->num_colbuf;// + 1;
543 pinf.pAttachments = att;
544 pinf.subpassCount = 1;
545 pinf.pSubpasses = &subpass;
547 if(vkCreateRenderPass(vkdev, &pinf, 0, &r->vkobj) != 0) {
548 fprintf(stderr, "failed to create render pass!\n");
558 int vk_create_fb(void)
561 struct framebuf framebuf = {0}, *fb = &framebuf;
564 framebufs = darr_alloc(0, sizeof *framebufs);
565 darr_push(framebufs, &framebuf); /* add dummy framebuffer */
568 for(i=1; i<darr_size(framebufs); i++) {
569 if(!framebufs[i].used) {
574 /* init framebuffer defaults */
575 memset(fb, 0, sizeof &fb);
578 if(fb == &framebuf) {
579 darr_push(framebufs, fb);
580 return darr_size(framebufs) - 1;
582 return fb - framebufs;
585 void vk_free_fb(int fb)
587 if(!framebufs || fb < 1 || fb >= darr_size(framebufs)) {
591 if(framebufs[fb].used && framebufs[fb].vkobj) {
592 vkDestroyFramebuffer(vkdev, framebufs[fb].vkobj, 0);
594 framebufs[fb].used = 0;
597 void vk_fb_size(int fb, int x, int y)
599 if(x != framebufs[fb].width || y != framebufs[fb].height) {
600 framebufs[fb].width = x;
601 framebufs[fb].height = y;
602 framebufs[fb].vkobj_valid = 0;
606 void vk_fb_rpass(int fb, int rpass)
608 if(rpass < 0 || rpass >= darr_size(rpasses) || !rpasses[rpass].used) {
609 fprintf(stderr, "vk_fb_rpass: %d is not a valid renderpass\n", rpass);
613 framebufs[fb].rpidx = rpass;
614 if(rpasses[rpass].vkobj_valid) {
615 framebufs[fb].vkrpass = rpasses[rpass].vkobj;
617 framebufs[fb].vkrpass = 0;
619 framebufs[fb].vkobj_valid = 0;
622 void vk_fb_images(int fb, int n, ...)
627 if(n > MAX_FB_IMGV) {
628 fprintf(stderr, "vk_fb_images: %d is too many images\n", n);
634 framebufs[fb].imgv[i] = va_arg(ap, VkImageView);
637 framebufs[fb].num_imgv = n;
638 framebufs[fb].vkobj_valid = 0;
641 VkFramebuffer vk_fb(int fb)
643 VkFramebufferCreateInfo fbinf;
649 if(!(rpass = vk_rpass(f->rpidx))) {
653 if(rpass != f->vkrpass || !f->vkobj_valid) {
656 vkDestroyFramebuffer(vkdev, f->vkobj, 0);
660 memset(&fbinf, 0, sizeof fbinf);
661 fbinf.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
662 fbinf.renderPass = rpass;
663 fbinf.attachmentCount = f->num_imgv;
664 fbinf.pAttachments = f->imgv;
665 fbinf.width = f->width;
666 fbinf.height = f->height;
669 if(vkCreateFramebuffer(vkdev, &fbinf, 0, &f->vkobj) != 0) {
670 fprintf(stderr, "vk_fb: failed to create framebuffer\n");
679 int vk_create_pipeln(void)
682 struct pipeline pipeln = {0}, *pp = &pipeln;
685 pipelines = darr_alloc(0, sizeof *pipelines);
686 darr_push(pipelines, &pipeln); /* add dummy pipeline */
689 for(i=1; i<darr_size(pipelines); i++) {
690 if(!pipelines[i].used) {
695 /* init pipeline defaults */
696 memset(pp, 0, sizeof *pp);
698 pp->vport.width = pp->scissor.extent.width = 640;
699 pp->vport.height = pp->scissor.extent.height = 480;
700 pp->vport.minDepth = 0.0f;
701 pp->vport.maxDepth = 1.0f;
702 pp->prim = VKPRIM_TRIANGLES;
703 pp->polymode = VK_POLYGON_MODE_FILL;
704 pp->cull = VK_CULL_MODE_BACK_BIT;
705 pp->frontface = VK_FRONT_FACE_COUNTER_CLOCKWISE;
706 pp->line_width = 1.0f;
707 pp->colorwr = 0xf; /* RGBA */
709 pp->zop = VK_COMPARE_OP_LESS;
713 pp->sop = VK_COMPARE_OP_ALWAYS;
714 pp->smask = 0xffffffff;
716 pp->srcblend = pp->srcblend_a = VK_BLEND_FACTOR_ONE;
717 pp->dstblend = pp->dstblend_a = VK_BLEND_FACTOR_ZERO;
720 darr_push(pipelines, pp);
721 return darr_size(pipelines) - 1;
723 return pp - pipelines;
726 void vk_free_pipeln(int pp)
728 if(!pipelines || pp < 1 || pp >= darr_size(pipelines)) {
732 if(pipelines[pp].used && pipelines[pp].vkobj) {
733 vkDestroyPipeline(vkdev, pipelines[pp].vkobj, 0);
735 pipelines[pp].used = 0;
738 void vk_pipeln_rpass(int pp, VkRenderPass rp)
740 struct pipeline *p = pipelines + pp;
745 void vk_pipeln_viewport(int pp, int x, int y, int width, int height)
747 struct pipeline *p = pipelines + pp;
750 p->vport.width = width;
751 p->vport.height = height;
755 void vk_pipeln_scissor(int pp, int x, int y, int width, int height)
757 struct pipeline *p = pipelines + pp;
758 p->scissor.offset.x = x;
759 p->scissor.offset.y = y;
760 p->scissor.extent.width = width;
761 p->scissor.extent.height = height;
765 void vk_pipeln_shader(int pp, int type, VkShaderModule sdr)
767 struct pipeline *p = pipelines + pp;
772 /* TODO: vertex input */
773 void vk_pipeln_prim(int pp, int prim)
775 struct pipeline *p = pipelines + pp;
780 void vk_pipeln_polymode(int pp, int mode)
782 struct pipeline *p = pipelines + pp;
787 void vk_pipeln_cull(int pp, int cull)
789 struct pipeline *p = pipelines + pp;
794 void vk_pipeln_frontface(int pp, int ff)
796 struct pipeline *p = pipelines + pp;
801 void vk_pipeln_linewidth(int pp, int w)
803 struct pipeline *p = pipelines + pp;
808 void vk_pipeln_multisample(int pp, int nsamples)
813 void vk_pipeln_colormask(int pp, int r, int g, int b, int a)
815 struct pipeline *p = pipelines + pp;
817 if(r) p->colorwr |= VK_COLOR_COMPONENT_R_BIT;
818 if(g) p->colorwr |= VK_COLOR_COMPONENT_G_BIT;
819 if(b) p->colorwr |= VK_COLOR_COMPONENT_B_BIT;
820 if(a) p->colorwr |= VK_COLOR_COMPONENT_A_BIT;
824 void vk_pipeln_depthmask(int pp, int z)
826 struct pipeline *p = pipelines + pp;
831 void vk_pipeln_stencilmask(int pp, int s)
833 struct pipeline *p = pipelines + pp;
838 void vk_pipeln_zbuffer(int pp, int enable)
840 struct pipeline *p = pipelines + pp;
845 void vk_pipeln_zbuffer_op(int pp, int op)
847 struct pipeline *p = pipelines + pp;
852 void vk_pipeln_stencil(int pp, int enable)
854 struct pipeline *p = pipelines + pp;
859 void vk_pipeln_stencil_op(int pp, int sfail, int zfail, int zpass)
861 struct pipeline *p = pipelines + pp;
868 void vk_pipeln_stencil_func(int pp, int op, unsigned int ref, unsigned int mask)
870 struct pipeline *p = pipelines + pp;
877 void vk_pipeln_blend(int pp, int enable)
879 struct pipeline *p = pipelines + pp;
884 void vk_pipeln_blendfunc(int pp, int src, int dst)
886 struct pipeline *p = pipelines + pp;
892 VkPipeline vk_pipeln(int pp)
895 struct pipeline *p = pipelines + pp;
896 VkPipelineShaderStageCreateInfo ssinf[VKSDR_MAX];
897 VkPipelineVertexInputStateCreateInfo vinp;
898 VkPipelineInputAssemblyStateCreateInfo vasm;
899 VkPipelineViewportStateCreateInfo vp;
900 VkPipelineRasterizationStateCreateInfo rast;
901 VkPipelineMultisampleStateCreateInfo msaa;
902 VkPipelineDepthStencilStateCreateInfo zst;
903 VkPipelineColorBlendAttachmentState bat;
904 VkPipelineColorBlendStateCreateInfo blend;
905 VkPipelineLayoutCreateInfo lay;
906 VkGraphicsPipelineCreateInfo pinf;
914 vkDestroyPipeline(vkdev, p->vkobj, 0);
918 memset(ssinf, 0, sizeof ssinf);
920 for(i=0; i<VKSDR_MAX; i++) {
922 ssinf[idx].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
923 ssinf[idx].stage = VKSDR_STAGE(i);
924 ssinf[idx].module = p->sdr[idx];
925 ssinf[idx].pName = "main";
931 memset(&vinp, 0, sizeof vinp);
932 vinp.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
934 memset(&vasm, 0, sizeof vasm);
935 vasm.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
936 vasm.topology = p->prim;
938 memset(&vp, 0, sizeof vp);
939 vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
940 vp.viewportCount = 1;
941 vp.pViewports = &p->vport;
943 vp.pScissors = &p->scissor;
945 memset(&rast, 0, sizeof rast);
946 rast.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
947 rast.polygonMode = p->polymode;
948 rast.lineWidth = p->line_width;
949 rast.cullMode = p->cull;
950 rast.frontFace = p->frontface;
953 memset(&msaa, 0, sizeof msaa);
954 msaa.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
955 msaa.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
956 msaa.minSampleShading = 1.0f;
958 memset(&zst, 0, sizeof zst);
959 zst.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
960 zst.depthTestEnable = p->zbuf;
961 zst.depthWriteEnable = p->depthwr;
962 zst.depthCompareOp = p->zop;
963 zst.stencilTestEnable = p->stencil;
964 zst.front.writeMask = p->stencilwr;
965 zst.front.failOp = p->sfail;
966 zst.front.passOp = p->szpass;
967 zst.front.depthFailOp = p->szfail;
968 zst.front.compareOp = p->sop;
969 zst.front.compareMask = p->smask;
970 zst.front.reference = p->sref;
971 zst.back = zst.front;
973 memset(&bat, 0, sizeof bat);
974 bat.colorWriteMask = p->colorwr;
975 bat.blendEnable = p->blend;
976 bat.srcColorBlendFactor = p->srcblend;
977 bat.dstColorBlendFactor = p->dstblend;
978 bat.colorBlendOp = VK_BLEND_OP_ADD; /* TODO */
979 bat.srcAlphaBlendFactor = p->srcblend_a;
980 bat.dstAlphaBlendFactor = p->dstblend_a;
982 memset(&blend, 0, sizeof blend);
983 blend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
984 blend.attachmentCount = 1;
985 blend.pAttachments = &bat;
988 memset(&lay, 0, sizeof lay);
989 lay.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
990 if(vkCreatePipelineLayout(vkdev, &lay, 0, &p->vkobj_layout) != 0) {
994 memset(&pinf, 0, sizeof pinf);
995 pinf.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
996 pinf.stageCount = num_sdr;
997 pinf.pStages = ssinf;
998 pinf.pVertexInputState = &vinp;
999 pinf.pInputAssemblyState = &vasm;
1000 pinf.pViewportState = &vp;
1001 pinf.pRasterizationState = &rast;
1002 pinf.pDepthStencilState = &zst;
1003 pinf.pMultisampleState = &msaa;
1004 pinf.pColorBlendState = &blend;
1005 pinf.layout = p->vkobj_layout;
1006 pinf.renderPass = p->rpass;
1007 pinf.basePipelineIndex = -1;
1009 if(vkCreateGraphicsPipelines(vkdev, 0, 1, &pinf, 0, &p->vkobj) != 0) {
1017 VkShaderModule vk_load_shader(const char *fname)
1022 VkShaderModuleCreateInfo sinf;
1025 if(!(fp = fopen(fname, "rb"))) {
1026 fprintf(stderr, "failed to open shader: %s: %s\n", fname, strerror(errno));
1029 fseek(fp, 0, SEEK_END);
1031 fseek(fp, 0, SEEK_SET);
1034 if(fread(buf, 1, sz, fp) < sz) {
1035 fprintf(stderr, "unexpected EOF while reading shader: %s\n", fname);
1041 memset(&sinf, 0, sizeof sinf);
1042 sinf.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
1046 if(vkCreateShaderModule(vkdev, &sinf, 0, &sdr) != 0) {
1047 fprintf(stderr, "failed to create shader from %s\n", fname);
1053 void vk_free_shader(VkShaderModule sdr)
1055 vkDestroyShaderModule(vkdev, sdr, 0);
1058 VkSemaphore vk_create_sem(void)
1061 VkSemaphoreCreateInfo sinf = {0};
1063 sinf.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1064 if(vkCreateSemaphore(vkdev, &sinf, 0, &sem) != 0) {
1070 void vk_free_sem(VkSemaphore sem)
1072 vkDestroySemaphore(vkdev, sem, 0);
1076 void vk_rect(VkRect2D *r, int x, int y, int w, int h)
1080 r->extent.width = w;
1081 r->extent.height = h;
1085 #define ARRSZ(arr) (sizeof arr / sizeof *arr)
1086 static const char *known_layer_list[] = {
1087 "VK_LAYER_KHRONOS_validation",
1088 "VK_LAYER_GOOGLE_threading",
1089 "VK_LAYER_LUNARG_parameter_validation",
1090 "VK_LAYER_LUNARG_object_tracker",
1091 "VK_LAYER_LUNARG_image",
1092 "VK_LAYER_LUNARG_core_validation",
1093 "VK_LAYER_LUNARG_swapchain",
1094 "VK_LAYER_GOOGLE_unique_objects"
1100 } known_instext_list[] = {
1101 {"VK_KHR_surface", 1},
1103 {"VK_KHR_win32_surface", 1},
1105 /*{"VK_KHR_xlib_surface", 1},*/
1106 {"VK_KHR_xcb_surface", 1},
1108 {"VK_KHR_debug_report", 0}
1114 } known_devext_list[] = {
1115 {"VK_KHR_swapchain", 1},
1116 {"VK_KHR_acceleration_structure", 0},
1117 {"VK_KHR_ray_tracing_pipeline", 0}
1120 static int create_instance(void)
1122 int i, nlayers = 0, next = 0;
1123 VkInstanceCreateInfo instinf;
1124 VkApplicationInfo appinf;
1125 const char *layers[ARRSZ(known_layer_list)];
1126 const char *ext[ARRSZ(known_instext_list)];
1129 vkEnumerateInstanceVersion(&apiver);
1130 printf("Vulkan API version: %d.%d.%d\n", (apiver >> 22) & 0x7f,
1131 (apiver >> 12) & 0x3ff, apiver & 0xfff);
1133 memset(&appinf, 0, sizeof appinf);
1134 appinf.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
1135 appinf.pApplicationName = "vkray";
1136 appinf.pEngineName = "vkray";
1137 appinf.apiVersion = apiver;
1139 vkEnumerateInstanceLayerProperties(&inst_layers_count, 0);
1140 inst_layers = malloc_nf(inst_layers_count * sizeof *inst_layers);
1141 vkEnumerateInstanceLayerProperties(&inst_layers_count, inst_layers);
1143 vkEnumerateInstanceExtensionProperties(0, &inst_ext_count, 0);
1144 inst_ext = malloc_nf(inst_ext_count * sizeof *inst_ext);
1145 vkEnumerateInstanceExtensionProperties(0, &inst_ext_count, inst_ext);
1147 printf("Layers:\n");
1148 for(i=0; i<inst_layers_count; i++) {
1149 printf(" - %s: %s\n", inst_layers[i].layerName, inst_layers[i].description);
1151 printf("Instance extensions:\n");
1152 for(i=0; i<inst_ext_count; i++) {
1153 printf(" - %s\n", inst_ext[i].extensionName);
1156 have_debug_report = have_ext(inst_ext, inst_ext_count, "VK_KHR_debug_report");
1158 for(i=0; i<ARRSZ(known_layer_list); i++) {
1159 if(have_inst_layer(known_layer_list[i])) {
1160 layers[nlayers++] = known_layer_list[i];
1163 for(i=0; i<ARRSZ(known_instext_list); i++) {
1164 if(have_ext(inst_ext, inst_ext_count, known_instext_list[i].name)) {
1165 ext[next++] = known_instext_list[i].name;
1166 } else if(known_instext_list[i].required) {
1167 fprintf(stderr, "Vulkan implementation lacks required instance extension: %s\n",
1168 known_instext_list[i].name);
1173 memset(&instinf, 0, sizeof instinf);
1174 instinf.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
1175 instinf.pApplicationInfo = &appinf;
1176 instinf.enabledLayerCount = nlayers;
1177 instinf.ppEnabledLayerNames = layers;
1178 instinf.enabledExtensionCount = next;
1179 instinf.ppEnabledExtensionNames = ext;
1180 if(vkCreateInstance(&instinf, 0, &vk) != 0) {
1181 fprintf(stderr, "failed to create vulkan instance\n");
1188 static int create_surface(void)
1191 VkXlibSurfaceCreateInfoKHR xinf = {0};
1192 xinf.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
1196 if(vkCreateXlibSurfaceKHR(vk, &xinf, 0, &vksurf) != 0) {
1197 fprintf(stderr, "failed to create Xlib window surface\n");
1201 VkXcbSurfaceCreateInfoKHR xinf = {0};
1202 xinf.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
1203 xinf.connection = XGetXCBConnection(dpy);
1204 xinf.window = (xcb_window_t)win;
1206 if(vkCreateXcbSurfaceKHR(vk, &xinf, 0, &vksurf) != 0) {
1207 fprintf(stderr, "failed to create XCB window surface\n");
1213 int choose_phys_dev(void)
1215 uint32_t i, num_pdev, score, best_score, best_dev;
1216 VkPhysicalDevice *pdev;
1217 VkPhysicalDeviceProperties pdevprop;
1220 vkEnumeratePhysicalDevices(vk, &num_pdev, 0);
1222 fprintf(stderr, "no vulkan devices found\n");
1225 pdev = malloc_nf(num_pdev * sizeof *pdev);
1226 vkEnumeratePhysicalDevices(vk, &num_pdev, pdev);
1228 printf("Found %d physical devices\n", num_pdev);
1232 for(i=0; i<num_pdev; i++) {
1233 if((score = eval_pdev_score(pdev[i])) && score > best_score) {
1238 vkGetPhysicalDeviceProperties(pdev[i], &pdevprop);
1239 printf(" %d: %s (score: %d)\n", i, pdevprop.deviceName, score);
1241 if(best_dev == -1) {
1242 fprintf(stderr, "no suitable vulkan device found\n");
1246 vkpdev = pdev[best_dev];
1248 if(qfam) free(qfam);
1250 vkGetPhysicalDeviceQueueFamilyProperties(vkpdev, &num_qfam, 0);
1251 qfam = malloc_nf(num_qfam * sizeof *qfam);
1252 vkGetPhysicalDeviceQueueFamilyProperties(vkpdev, &num_qfam, qfam);
1259 static int create_device(void)
1262 VkDeviceQueueCreateInfo qinf[MAX_INIT_QUEUE] = {0};
1263 VkPhysicalDeviceFeatures feat = {0};
1264 VkDeviceCreateInfo devinf = {0};
1265 const char *ext[ARRSZ(known_devext_list) + 16];
1266 int i, j, num_ext, qfam, totalq;
1268 vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, 0);
1269 dev_ext = malloc_nf(dev_ext_count * sizeof *dev_ext);
1270 vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, dev_ext);
1273 for(i=0; i<ARRSZ(known_devext_list); i++) {
1274 if(have_ext(dev_ext, dev_ext_count, known_devext_list[i].name)) {
1275 ext[num_ext++] = known_devext_list[i].name;
1276 } else if(known_devext_list[i].required) {
1277 fprintf(stderr, "Vulkan device lacks required extension: %s\n",
1278 known_devext_list[i].name);
1283 if(initflags & VKINIT_RAY) {
1284 if(have_ext(dev_ext, dev_ext_count, "VK_KHR_acceleration_structure") &&
1285 have_ext(dev_ext, dev_ext_count, "VK_KHR_ray_tracing_pipeline")) {
1286 ext[num_ext++] = "VK_KHR_acceleration_structure";
1287 ext[num_ext++] = "VK_KHR_ray_tracing_pipeline";
1289 initflags &= ~VKINIT_RAY;
1294 for(i=0; i<num_initq; i++) {
1295 totalq += initq[i].count;
1298 fprintf(stderr, "create_device: arbitrary limit of total queues exceeded (%d)\n", totalq);
1301 prio = alloca(totalq * sizeof *prio);
1303 for(i=0; i<num_initq; i++) {
1304 if((qfam = vk_find_qfamily(initq[i].flags)) == -1) {
1305 fprintf(stderr, "create_device: failed to find queue family (flags: 0x%2x)\n",
1309 initq[i].qfam = qfam;
1310 initq[i].cmdpool = 0;
1312 qinf[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1313 qinf[i].queueFamilyIndex = qfam;
1314 qinf[i].queueCount = initq[i].count;
1315 qinf[i].pQueuePriorities = prio;
1316 for(j=0; j<initq[i].count; i++) {
1317 *prio++ = 1.0f; /* all queue priorities 1 */
1321 devinf.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
1322 devinf.pQueueCreateInfos = qinf;
1323 devinf.queueCreateInfoCount = num_initq;
1324 devinf.pEnabledFeatures = &feat;
1325 devinf.enabledExtensionCount = num_ext;
1326 devinf.ppEnabledExtensionNames = ext;
1328 if(vkCreateDevice(vkpdev, &devinf, 0, &vkdev) != 0) {
1329 fprintf(stderr, "failed to create vulkan device\n");
1335 static int create_swapchain(void)
1339 VkSwapchainCreateInfoKHR scinf = {0};
1340 VkImageViewCreateInfo ivinf;
1342 if(vksc_extent.width <= 0 || vksc_extent.height <= 0) {
1346 scinf.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1347 scinf.surface = vksurf;
1348 scinf.minImageCount = 2;
1349 scinf.imageFormat = vksurf_fmt[vksurf_selfmt].format;
1350 scinf.imageColorSpace = vksurf_fmt[vksurf_selfmt].colorSpace;
1351 scinf.imageExtent = vksc_extent;
1352 scinf.imageArrayLayers = 1;
1353 scinf.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1354 scinf.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1355 scinf.preTransform = vksurf_caps.currentTransform;
1356 scinf.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1357 scinf.presentMode = VK_PRESENT_MODE_FIFO_KHR;
1358 scinf.clipped = VK_TRUE;
1360 if(vkCreateSwapchainKHR(vkdev, &scinf, 0, &vksc) != 0) {
1361 fprintf(stderr, "failed to create swapchain\n");
1365 if(!vksc_img || vksc_numimg != num) {
1367 vkGetSwapchainImagesKHR(vkdev, vksc, &num, 0);
1368 vksc_img = malloc_nf(num * sizeof *vksc_img);
1369 vkGetSwapchainImagesKHR(vkdev, vksc, &num, vksc_img);
1371 if(!vksc_view || vksc_numimg != num) {
1373 vksc_view = malloc_nf(num * sizeof *vksc_view);
1377 for(i=0; i<num; i++) {
1378 memset(&ivinf, 0, sizeof ivinf);
1379 ivinf.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1380 ivinf.image = vksc_img[i];
1381 ivinf.format = vksurf_fmt[vksurf_selfmt].format;
1382 ivinf.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
1383 ivinf.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
1384 ivinf.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
1385 ivinf.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
1386 ivinf.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1387 ivinf.subresourceRange.levelCount = 1;
1388 ivinf.subresourceRange.layerCount = 1;
1389 ivinf.viewType = VK_IMAGE_VIEW_TYPE_2D;
1391 if(vkCreateImageView(vkdev, &ivinf, 0, vksc_view + i) != 0) {
1392 fprintf(stderr, "failed to create image view (%d)\n", i);
1399 static int create_default_cmdbuf(void)
1402 VkCommandPool cmdpool;
1403 VkCommandBufferAllocateInfo cbinf = {0};
1405 assert(!vksc_cmdbuf);
1407 if((qfam = vk_find_qfamily(VKQ_GFX | VKQ_PRESENT)) == -1) {
1408 fprintf(stderr, "failed to find a gfx|present capable queue family\n");
1411 if(!(cmdpool = find_cmdpool(qfam))) {
1412 fprintf(stderr, "failed to find usable command pool for default command buffers\n");
1415 if(!(vksc_cmdbuf = malloc(vksc_numimg * sizeof *vksc_cmdbuf))) {
1416 fprintf(stderr, "failed to allocate %d command buffers for the swap-chain\n", vksc_numimg);
1420 cbinf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1421 cbinf.commandPool = cmdpool;
1422 cbinf.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1423 cbinf.commandBufferCount = vksc_numimg;
1425 if(vkAllocateCommandBuffers(vkdev, &cbinf, vksc_cmdbuf) != 0) {
1426 fprintf(stderr, "failed to create %d command buffers for the swap-chain\n", vksc_numimg);
1432 static int eval_pdev_score(VkPhysicalDevice dev)
1435 uint32_t i, num_fmt, num_qfam, num_ext;
1436 VkQueueFamilyProperties *qfam;
1437 VkExtensionProperties *ext;
1438 VkPhysicalDeviceProperties prop;
1439 VkPhysicalDeviceFeatures feat;
1440 VkSurfaceFormatKHR *sfmt;
1443 vkGetPhysicalDeviceProperties(dev, &prop);
1444 vkGetPhysicalDeviceFeatures(dev, &feat);
1446 /* check if we have the swapchain extension */
1447 vkEnumerateDeviceExtensionProperties(dev, 0, &num_ext, 0);
1448 ext = malloc_nf(num_ext * sizeof *ext);
1449 vkEnumerateDeviceExtensionProperties(dev, 0, &num_ext, ext);
1451 if(!have_ext(ext, num_ext, "VK_KHR_swapchain")) {
1456 /* populate format and present modes arrays, and make sure we have some of each */
1457 vkGetPhysicalDeviceSurfaceFormatsKHR(dev, vksurf, &num_fmt, 0);
1462 sfmt = malloc_nf(num_fmt * sizeof *sfmt);
1463 vkGetPhysicalDeviceSurfaceFormatsKHR(dev, vksurf, &num_fmt, sfmt);
1465 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, vksurf, &vksurf_caps);
1467 /* find a queue family which can do graphics and can present */
1468 vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, 0);
1469 qfam = malloc_nf(num_qfam * sizeof *qfam);
1470 vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, qfam);
1472 for(i=0; i<num_qfam; i++) {
1473 vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, vksurf, &can_pres);
1474 if(qfam[i].queueCount && (qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && can_pres) {
1479 switch(prop.deviceType) {
1480 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
1483 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
1486 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
1493 if(initflags & VKINIT_RAY) {
1494 if(have_ext(ext, num_ext, "VK_KHR_acceleration_structure") &&
1495 have_ext(ext, num_ext, "VK_KHR_ray_tracing_pipeline")) {
1506 static int choose_pixfmt(void)
1508 static const VkFormat pref[] = {
1509 VK_FORMAT_B8G8R8_UNORM,
1510 VK_FORMAT_R8G8B8_UNORM,
1511 VK_FORMAT_B8G8R8A8_UNORM,
1512 VK_FORMAT_R8G8B8A8_UNORM
1517 vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, 0);
1518 if(!num_fmt) return -1;
1519 vksurf_fmt = malloc_nf(num_fmt * sizeof *vksurf_fmt);
1520 vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, vksurf_fmt);
1523 for(i=0; i<num_fmt; i++) {
1524 if(vksurf_fmt[i].colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
1527 for(j=0; j<sizeof pref / sizeof *pref; j++) {
1528 if(vksurf_fmt[i].format == pref[j]) {
1530 vksurf_numfmt = num_fmt;
1541 static int have_inst_layer(const char *name)
1544 for(i=0; i<inst_layers_count; i++) {
1545 if(strcmp(inst_layers[i].layerName, name) == 0) {
1552 static int have_ext(VkExtensionProperties *ext, int next, const char *name)
1555 for(i=0; i<next; i++) {
1556 if(strcmp(ext[i].extensionName, name) == 0) {