--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+#include <vulkan/vulkan.h>
+#include "vk.h"
+#include "util.h"
+#include "darray.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <vulkan/vulkan_win32.h>
+#else
+/*#include <vulkan/vulkan_xlib.h>*/
+#include <X11/Xlib-xcb.h>
+#include <vulkan/vulkan_xcb.h>
+#endif
+
+struct rpass {
+ int used;
+ int fmt;
+ int zfmt;
+ int num_colbuf;
+ int num_samples;
+ int clear;
+
+ int vkobj_valid;
+ VkRenderPass vkobj;
+};
+
+#define MAX_FB_IMGV 8
+struct framebuf {
+ int used;
+ int width, height;
+
+ /* if rpasses[rpidx].vkobj != vkrpass, the framebuf is invalid */
+ int rpidx;
+ VkRenderPass vkrpass;
+
+ VkImageView imgv[MAX_FB_IMGV];
+ int num_imgv;
+
+ int vkobj_valid;
+ VkFramebuffer vkobj;
+};
+
+struct pipeline {
+ int used;
+ VkViewport vport;
+ VkRect2D scissor;
+ VkShaderModule sdr[VKSDR_MAX];
+ VkPrimitiveTopology prim;
+ VkPolygonMode polymode;
+ float line_width;
+ VkCullModeFlags cull;
+ VkFrontFace frontface;
+ VkColorComponentFlags colorwr;
+ int zbuf, depthwr;
+ VkCompareOp zop;
+ int stencil, stencilwr;
+ VkStencilOp sfail, szfail, szpass;
+ VkCompareOp sop;
+ unsigned int sref, smask;
+ int blend;
+ VkBlendFactor srcblend, dstblend, srcblend_a, dstblend_a;
+
+ VkRenderPass rpass;
+
+ int vkobj_valid;
+ VkPipeline vkobj;
+ VkPipelineLayout vkobj_layout; /* TODO probably best to split this */
+};
+
+
+static struct rpass *rpasses;
+static struct framebuf *framebufs;
+static struct pipeline *pipelines;
+
+
+static int create_instance(void);
+static int create_surface(void);
+static int choose_phys_dev(void);
+static int create_device(void);
+static int create_swapchain(void);
+static int create_default_cmdbuf(void);
+
+static int choose_pixfmt(void);
+static int eval_pdev_score(VkPhysicalDevice dev);
+static int have_inst_layer(const char *name);
+static int have_ext(VkExtensionProperties *ext, int next, const char *name);
+
+#ifdef _WIN32
+static HINSTANCE hinst;
+static HWND win;
+#else
+static Display *dpy;
+static Window win;
+#endif
+static int initflags;
+#define MAX_INIT_QUEUE 32
+static struct {
+ unsigned int flags;
+ int count;
+ int qfam;
+ VkCommandPool cmdpool;
+} initq[MAX_INIT_QUEUE];
+static int num_initq;
+
+static VkInstance vk;
+static VkPhysicalDevice vkpdev;
+static VkQueueFamilyProperties *qfam;
+static uint32_t num_qfam;
+static VkDevice vkdev;
+static VkSurfaceKHR vksurf;
+static VkSurfaceCapabilitiesKHR vksurf_caps;
+static int vksurf_numfmt, vksurf_selfmt;
+static VkSurfaceFormatKHR *vksurf_fmt;
+static VkSwapchainKHR vksc;
+static int vksc_numimg;
+static VkImage *vksc_img;
+static VkExtent2D vksc_extent;
+static VkImageView *vksc_view;
+static VkCommandBuffer *vksc_cmdbuf; /* default command buffers (vksc_numimg) */
+
+static VkLayerProperties *inst_layers;
+static VkExtensionProperties *inst_ext, *dev_ext;
+static uint32_t inst_ext_count, dev_ext_count, inst_layers_count;
+
+static VkPhysicalDevice *pdev_list;
+static uint32_t num_pdev;
+
+static int have_raytrace, have_debug_report;
+
+#ifdef _WIN32
+void vk_init_win(HINSTANCE hi, HWND w)
+{
+ hinst = hi;
+ win = w;
+}
+#else
+void vk_init_xwin(Display *d, Window w)
+{
+ dpy = d;
+ win = w;
+}
+#endif
+
+void vk_init_queue(unsigned int qflags, int count)
+{
+ int i;
+
+ for(i=0; i<num_initq; i++) {
+ if(initq[i].flags == qflags) {
+ initq[i].count += count;
+ return;
+ }
+ }
+
+ if(num_initq >= MAX_INIT_QUEUE) {
+ fprintf(stderr, "vk_init_queue: too many queues\n");
+ return;
+ }
+ initq[num_initq].flags = qflags;
+ initq[num_initq].count = count;
+ num_initq++;
+}
+
+int vk_init(unsigned int flags, unsigned int *usedflags)
+{
+ if(!num_initq) {
+ vk_init_queue(VKQ_GFX | VKQ_PRESENT, 1);
+ }
+
+ initflags = flags;
+ if(create_instance() == -1) return -1;
+ if(create_surface() == -1) return -1;
+ if(choose_phys_dev() == -1) return -1;
+ if(create_device() == -1) return -1;
+
+ if(initflags != flags && !usedflags) {
+ vk_cleanup();
+ return -1;
+ }
+
+ if(usedflags) {
+ *usedflags = initflags;
+ }
+ return 0;
+}
+
+void vk_cleanup(void)
+{
+ int i;
+
+ free(vksc_img);
+ vksc_img = 0;
+ free(vksc_view);
+ vksc_view = 0;
+ if(vksc_view) {
+ for(i=0; i<vksc_numimg; i++) {
+ vkDestroyImageView(vkdev, vksc_view[i], 0);
+ }
+ vksc_view = 0;
+ }
+ if(vksc) {
+ vkDestroySwapchainKHR(vkdev, vksc, 0);
+ vksc = 0;
+ }
+ if(vkdev) {
+ vkDestroyDevice(vkdev, 0);
+ vkdev = 0;
+ }
+ if(vksurf) {
+ vkDestroySurfaceKHR(vk, vksurf, 0);
+ vksurf = 0;
+ }
+ if(vk) {
+ vkDestroyInstance(vk, 0);
+ vk = 0;
+ }
+ free(inst_layers);
+ inst_layers = 0;
+ free(inst_ext);
+ inst_ext = 0;
+ free(dev_ext);
+ dev_ext = 0;
+ free(pdev_list);
+ pdev_list = 0;
+}
+
+int vk_reshape(int xsz, int ysz)
+{
+ int i;
+
+ if(vksc && vksc_extent.width == xsz && vksc_extent.height == ysz) {
+ return 0;
+ }
+
+ if(vksc_view) {
+ for(i=0; i<vksc_numimg; i++) {
+ vkDestroyImageView(vkdev, vksc_view[i], 0);
+ }
+ }
+ if(vksc) vkDestroySwapchainKHR(vkdev, vksc, 0);
+
+ vksc_extent.width = xsz;
+ vksc_extent.height = ysz;
+
+ if(create_swapchain() == -1) return -1;
+
+ if(!vksc_cmdbuf) {
+ if(create_default_cmdbuf() == -1) return -1;
+ }
+
+ /* TODO create depth/stencil buffers as needed (initflags) */
+ return 0;
+}
+
+int vk_num_swap_images(void)
+{
+ return vksc_numimg;
+}
+
+VkImageView vk_swap_image(int idx)
+{
+ return vksc_view[idx];
+}
+
+int vk_next_swap_image(VkSemaphore sem)
+{
+ uint32_t idx;
+ if(vkAcquireNextImageKHR(vkdev, vksc, UINT64_MAX, sem, 0, &idx) != 0) {
+ return -1;
+ }
+ return (int)idx;
+}
+
+int vk_submit(VkQueue q, VkCommandBuffer cmdbuf, VkSemaphore semwait, VkSemaphore semsig)
+{
+ /* TODO: investigate if we need to expose the wait stage */
+ VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkSubmitInfo sinf = {0};
+ sinf.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ sinf.waitSemaphoreCount = semwait ? 1 : 0;
+ sinf.pWaitSemaphores = &semwait;
+ sinf.pWaitDstStageMask = &wait_stage;
+ sinf.commandBufferCount = 1;
+ sinf.pCommandBuffers = &cmdbuf;
+ sinf.signalSemaphoreCount = semsig ? 1 : 0;
+ sinf.pSignalSemaphores = &semsig;
+
+ if(vkQueueSubmit(q, 1, &sinf, 0) != 0) {
+ fprintf(stderr, "failed to submit command buffer\n");
+ return -1;
+ }
+ return 0;
+}
+
+int vk_present(VkQueue q, int imgid, VkSemaphore semwait)
+{
+ VkPresentInfoKHR pinf = {0};
+
+ pinf.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+ pinf.waitSemaphoreCount = semwait ? 1 : 0;
+ pinf.pWaitSemaphores = &semwait;
+ pinf.swapchainCount = 1;
+ pinf.pSwapchains = &vksc;
+ pinf.pImageIndices = (unsigned int*)&imgid;
+
+ if(vkQueuePresentKHR(q, &pinf) != 0) {
+ fprintf(stderr, "present failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+int vk_find_qfamily(unsigned int flags)
+{
+ int i, famidx = -1;
+ VkBool32 can_pres;
+
+ if(!qfam) return -1; /* not initialized I guess... */
+
+ for(i=0; i<num_qfam; i++) {
+ vkGetPhysicalDeviceSurfaceSupportKHR(vkpdev, i, vksurf, &can_pres);
+
+ if((flags & VKQ_PRESENT) && !can_pres) {
+ continue;
+ }
+ if((flags & VKQ_GFX) && !(qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) {
+ continue;
+ }
+ if((flags & VKQ_COMPUTE) && !(qfam[i].queueFlags & VK_QUEUE_COMPUTE_BIT)) {
+ continue;
+ }
+
+ return i; /* found a suitabe queue family */
+ }
+
+ return -1;
+}
+
+VkQueue vk_getq_fam(int fam, int n)
+{
+ VkQueue q;
+
+ if(fam < 0) return 0;
+ if(n < 0 || n >= qfam[fam].queueCount) {
+ fprintf(stderr, "vk_getq_fam: invalid index %d, family %d has %d queues\n",
+ n, fam, qfam[fam].queueCount);
+ return 0;
+ }
+
+ vkGetDeviceQueue(vkdev, fam, n, &q);
+ return q;
+}
+
+VkQueue vk_getq(unsigned int flags, int n)
+{
+ return vk_getq_fam(vk_find_qfamily(flags), n);
+}
+
+static VkCommandPool find_cmdpool(int qfam)
+{
+ int i;
+ VkCommandPoolCreateInfo pinf;
+
+ for(i=0; i<num_initq; i++) {
+ if(initq[i].qfam == qfam) {
+ if(!initq[i].cmdpool) {
+ /* allocate command pool for this queue family */
+ memset(&pinf, 0, sizeof pinf);
+ pinf.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ pinf.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+ pinf.queueFamilyIndex = qfam;
+
+ if(vkCreateCommandPool(vkdev, &pinf, 0, &initq[i].cmdpool) != 0) {
+ fprintf(stderr, "ck_create_cmdbuf: failed to create command buffer pool\n");
+ return 0;
+ }
+ }
+ return initq[i].cmdpool;
+ }
+ }
+
+ fprintf(stderr, "vk_create_cmdbuf: failed to find command pool for queue family: %d\n", qfam);
+ return 0;
+}
+
+VkCommandBuffer vk_create_cmdbuf_fam(int qfam, int level)
+{
+ VkCommandBufferAllocateInfo inf = {0};
+ VkCommandBuffer cmdbuf;
+ VkCommandPool cmdpool;
+
+ if(!(cmdpool = find_cmdpool(qfam))) {
+ return 0;
+ }
+
+ inf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ inf.commandPool = cmdpool;
+ inf.level = level;
+ inf.commandBufferCount = 1;
+
+ if(vkAllocateCommandBuffers(vkdev, &inf, &cmdbuf) != 0) {
+ fprintf(stderr, "vk_create_cmdbuf: failed to allocate command buffer\n");
+ return 0;
+ }
+ return cmdbuf;
+}
+
+VkCommandBuffer vk_create_cmdbuf(unsigned int qflags, int level)
+{
+ int qfam;
+
+ if((qfam = vk_find_qfamily(qflags)) == -1) {
+ fprintf(stderr, "vk_create_cmdbuf: failed to find matching queue family\n");
+ return 0;
+ }
+ return vk_create_cmdbuf_fam(qfam, level);
+}
+
+VkCommandBuffer vk_get_cmdbuf(int imgid)
+{
+ if(imgid < 0 || imgid >= vksc_numimg) {
+ fprintf(stderr, "vk_get_cmdbuf: invalid id %d, swap chain has %d images\n",
+ imgid, vksc_numimg);
+ return 0;
+ }
+ return vksc_cmdbuf[imgid];
+}
+
+int vk_create_rpass(void)
+{
+ int i;
+ struct rpass rpass = {0}, *rp = &rpass;
+
+ if(!rpasses) {
+ rpasses = darr_alloc(0, sizeof *rpasses);
+ darr_push(rpasses, &rpass); /* add dummy rpass */
+ }
+
+ for(i=1; i<darr_size(rpasses); i++) {
+ if(!rpasses[i].used) {
+ rp = rpasses + i;
+ }
+ }
+
+ /* init renderpass defaults */
+ rp->used = 1;
+ rp->fmt = vksurf_fmt[vksurf_selfmt].format;
+ rp->zfmt = VK_FORMAT_D24_UNORM_S8_UINT;
+ rp->num_colbuf = 1;
+ rp->num_samples = 1;
+ rp->clear = 1;
+ rp->vkobj_valid = 0;
+ rp->vkobj = 0;
+
+ if(rp == &rpass) {
+ darr_push(rpasses, rp);
+ return darr_size(rpasses) - 1;
+ }
+ return rp - rpasses;
+}
+
+void vk_free_rpass(int rp)
+{
+ if(!rpasses || rp < 1 || rp >= darr_size(rpasses)) {
+ return;
+ }
+
+ if(rpasses[rp].used && rpasses[rp].vkobj) {
+ vkDestroyRenderPass(vkdev, rpasses[rp].vkobj, 0);
+ }
+ rpasses[rp].used = 0;
+}
+
+void vk_rpass_colorbuf(int rp, int fmt, int n)
+{
+ rpasses[rp].fmt = fmt;
+ rpasses[rp].num_colbuf = n;
+ rpasses[rp].vkobj_valid = 0;
+}
+
+void vk_rpass_msaa(int rp, int nsamp)
+{
+ rpasses[rp].num_samples = nsamp;
+ rpasses[rp].vkobj_valid = 0;
+}
+
+void vk_rpass_clear(int rp, int clear)
+{
+ rpasses[rp].clear = clear;
+ rpasses[rp].vkobj_valid = 0;
+}
+
+VkRenderPass vk_rpass(int rp)
+{
+ int i, zidx;
+ struct rpass *r;
+ VkAttachmentDescription att[17];
+ VkAttachmentReference catref[16], zatref;
+ VkSubpassDescription subpass;
+ VkRenderPassCreateInfo pinf;
+
+ r = rpasses + rp;
+
+ if(!r->vkobj_valid) {
+ if(r->vkobj) {
+ vkDestroyRenderPass(vkdev, r->vkobj, 0);
+ r->vkobj = 0;
+ }
+
+ zidx = r->num_colbuf;
+ memset(att, 0, sizeof att);
+ for(i=0; i<r->num_colbuf; i++) {
+ att[i].format = r->fmt;
+ att[i].samples = r->num_samples;
+ att[i].loadOp = r->clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ att[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ att[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ att[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ att[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ att[i].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ }
+ /*
+ att[zidx].format = r->zfmt;
+ att[zidx].samples = 1;
+ att[zidx].loadOp = r->clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ att[zidx].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ att[zidx].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ att[zidx].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ att[zidx].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ att[zidx].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ */
+
+ for(i=0; i<r->num_colbuf; i++) {
+ catref[i].attachment = i;
+ catref[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ }
+ /*
+ zatref.attachment = zidx;
+ zatref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ */
+
+ memset(&subpass, 0, sizeof subpass);
+ subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpass.colorAttachmentCount = r->num_colbuf;
+ subpass.pColorAttachments = catref;
+ subpass.pDepthStencilAttachment = 0;//&zatref;
+
+ memset(&pinf, 0, sizeof pinf);
+ pinf.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ pinf.attachmentCount = r->num_colbuf;// + 1;
+ pinf.pAttachments = att;
+ pinf.subpassCount = 1;
+ pinf.pSubpasses = &subpass;
+
+ if(vkCreateRenderPass(vkdev, &pinf, 0, &r->vkobj) != 0) {
+ fprintf(stderr, "failed to create render pass!\n");
+ return 0;
+ }
+ r->vkobj_valid = 1;
+ }
+
+ return r->vkobj;
+}
+
+
+int vk_create_fb(void)
+{
+ int i;
+ struct framebuf framebuf = {0}, *fb = &framebuf;
+
+ if(!framebufs) {
+ framebufs = darr_alloc(0, sizeof *framebufs);
+ darr_push(framebufs, &framebuf); /* add dummy framebuffer */
+ }
+
+ for(i=1; i<darr_size(framebufs); i++) {
+ if(!framebufs[i].used) {
+ fb = framebufs + i;
+ }
+ }
+
+ /* init framebuffer defaults */
+ memset(fb, 0, sizeof &fb);
+ fb->used = 1;
+
+ if(fb == &framebuf) {
+ darr_push(framebufs, fb);
+ return darr_size(framebufs) - 1;
+ }
+ return fb - framebufs;
+}
+
+void vk_free_fb(int fb)
+{
+ if(!framebufs || fb < 1 || fb >= darr_size(framebufs)) {
+ return;
+ }
+
+ if(framebufs[fb].used && framebufs[fb].vkobj) {
+ vkDestroyFramebuffer(vkdev, framebufs[fb].vkobj, 0);
+ }
+ framebufs[fb].used = 0;
+}
+
+void vk_fb_size(int fb, int x, int y)
+{
+ if(x != framebufs[fb].width || y != framebufs[fb].height) {
+ framebufs[fb].width = x;
+ framebufs[fb].height = y;
+ framebufs[fb].vkobj_valid = 0;
+ }
+}
+
+void vk_fb_rpass(int fb, int rpass)
+{
+ if(rpass < 0 || rpass >= darr_size(rpasses) || !rpasses[rpass].used) {
+ fprintf(stderr, "vk_fb_rpass: %d is not a valid renderpass\n", rpass);
+ return;
+ }
+
+ framebufs[fb].rpidx = rpass;
+ if(rpasses[rpass].vkobj_valid) {
+ framebufs[fb].vkrpass = rpasses[rpass].vkobj;
+ } else {
+ framebufs[fb].vkrpass = 0;
+ }
+ framebufs[fb].vkobj_valid = 0;
+}
+
+void vk_fb_images(int fb, int n, ...)
+{
+ int i;
+ va_list ap;
+
+ if(n > MAX_FB_IMGV) {
+ fprintf(stderr, "vk_fb_images: %d is too many images\n", n);
+ n = MAX_FB_IMGV;
+ }
+
+ va_start(ap, n);
+ for(i=0; i<n; i++) {
+ framebufs[fb].imgv[i] = va_arg(ap, VkImageView);
+ }
+ va_end(ap);
+ framebufs[fb].num_imgv = n;
+ framebufs[fb].vkobj_valid = 0;
+}
+
+VkFramebuffer vk_fb(int fb)
+{
+ VkFramebufferCreateInfo fbinf;
+ VkRenderPass rpass;
+ struct framebuf *f;
+
+ f = framebufs + fb;
+
+ if(!(rpass = vk_rpass(f->rpidx))) {
+ return 0;
+ }
+
+ if(rpass != f->vkrpass || !f->vkobj_valid) {
+ f->vkrpass = rpass;
+ if(f->vkobj) {
+ vkDestroyFramebuffer(vkdev, f->vkobj, 0);
+ f->vkobj = 0;
+ }
+
+ memset(&fbinf, 0, sizeof fbinf);
+ fbinf.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ fbinf.renderPass = rpass;
+ fbinf.attachmentCount = f->num_imgv;
+ fbinf.pAttachments = f->imgv;
+ fbinf.width = f->width;
+ fbinf.height = f->height;
+ fbinf.layers = 1;
+
+ if(vkCreateFramebuffer(vkdev, &fbinf, 0, &f->vkobj) != 0) {
+ fprintf(stderr, "vk_fb: failed to create framebuffer\n");
+ return 0;
+ }
+ f->vkobj_valid = 1;
+ }
+ return f->vkobj;
+}
+
+
+int vk_create_pipeln(void)
+{
+ int i;
+ struct pipeline pipeln = {0}, *pp = &pipeln;
+
+ if(!pipelines) {
+ pipelines = darr_alloc(0, sizeof *pipelines);
+ darr_push(pipelines, &pipeln); /* add dummy pipeline */
+ }
+
+ for(i=1; i<darr_size(pipelines); i++) {
+ if(!pipelines[i].used) {
+ pp = pipelines + i;
+ }
+ }
+
+ /* init pipeline defaults */
+ memset(pp, 0, sizeof *pp);
+ pp->used = 1;
+ pp->vport.width = pp->scissor.extent.width = 640;
+ pp->vport.height = pp->scissor.extent.height = 480;
+ pp->vport.minDepth = 0.0f;
+ pp->vport.maxDepth = 1.0f;
+ pp->prim = VKPRIM_TRIANGLES;
+ pp->polymode = VK_POLYGON_MODE_FILL;
+ pp->cull = VK_CULL_MODE_BACK_BIT;
+ pp->frontface = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ pp->line_width = 1.0f;
+ pp->colorwr = 0xf; /* RGBA */
+ pp->zbuf = 1;
+ pp->zop = VK_COMPARE_OP_LESS;
+ pp->depthwr = 1;
+ pp->stencil = 0;
+ pp->stencilwr = 1;
+ pp->sop = VK_COMPARE_OP_ALWAYS;
+ pp->smask = 0xffffffff;
+ pp->blend = 0;
+ pp->srcblend = pp->srcblend_a = VK_BLEND_FACTOR_ONE;
+ pp->dstblend = pp->dstblend_a = VK_BLEND_FACTOR_ZERO;
+
+ if(pp == &pipeln) {
+ darr_push(pipelines, pp);
+ return darr_size(pipelines) - 1;
+ }
+ return pp - pipelines;
+}
+
+void vk_free_pipeln(int pp)
+{
+ if(!pipelines || pp < 1 || pp >= darr_size(pipelines)) {
+ return;
+ }
+
+ if(pipelines[pp].used && pipelines[pp].vkobj) {
+ vkDestroyPipeline(vkdev, pipelines[pp].vkobj, 0);
+ }
+ pipelines[pp].used = 0;
+}
+
+void vk_pipeln_rpass(int pp, VkRenderPass rp)
+{
+ struct pipeline *p = pipelines + pp;
+ p->rpass = rp;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_viewport(int pp, int x, int y, int width, int height)
+{
+ struct pipeline *p = pipelines + pp;
+ p->vport.x = x;
+ p->vport.y = y;
+ p->vport.width = width;
+ p->vport.height = height;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_scissor(int pp, int x, int y, int width, int height)
+{
+ struct pipeline *p = pipelines + pp;
+ p->scissor.offset.x = x;
+ p->scissor.offset.y = y;
+ p->scissor.extent.width = width;
+ p->scissor.extent.height = height;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_shader(int pp, int type, VkShaderModule sdr)
+{
+ struct pipeline *p = pipelines + pp;
+ p->sdr[type] = sdr;
+ p->vkobj_valid = 0;
+}
+
+/* TODO: vertex input */
+void vk_pipeln_prim(int pp, int prim)
+{
+ struct pipeline *p = pipelines + pp;
+ p->prim = prim;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_polymode(int pp, int mode)
+{
+ struct pipeline *p = pipelines + pp;
+ p->polymode = mode;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_cull(int pp, int cull)
+{
+ struct pipeline *p = pipelines + pp;
+ p->cull = cull;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_frontface(int pp, int ff)
+{
+ struct pipeline *p = pipelines + pp;
+ p->frontface = ff;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_linewidth(int pp, int w)
+{
+ struct pipeline *p = pipelines + pp;
+ p->line_width = w;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_multisample(int pp, int nsamples)
+{
+ /* TODO */
+}
+
+void vk_pipeln_colormask(int pp, int r, int g, int b, int a)
+{
+ struct pipeline *p = pipelines + pp;
+ p->colorwr = 0;
+ if(r) p->colorwr |= VK_COLOR_COMPONENT_R_BIT;
+ if(g) p->colorwr |= VK_COLOR_COMPONENT_G_BIT;
+ if(b) p->colorwr |= VK_COLOR_COMPONENT_B_BIT;
+ if(a) p->colorwr |= VK_COLOR_COMPONENT_A_BIT;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_depthmask(int pp, int z)
+{
+ struct pipeline *p = pipelines + pp;
+ p->depthwr = z;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_stencilmask(int pp, int s)
+{
+ struct pipeline *p = pipelines + pp;
+ p->stencilwr = s;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_zbuffer(int pp, int enable)
+{
+ struct pipeline *p = pipelines + pp;
+ p->zbuf = enable;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_zbuffer_op(int pp, int op)
+{
+ struct pipeline *p = pipelines + pp;
+ p->zop = op;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_stencil(int pp, int enable)
+{
+ struct pipeline *p = pipelines + pp;
+ p->stencil = enable;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_stencil_op(int pp, int sfail, int zfail, int zpass)
+{
+ struct pipeline *p = pipelines + pp;
+ p->sfail = sfail;
+ p->szfail = zfail;
+ p->szpass = zpass;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_stencil_func(int pp, int op, unsigned int ref, unsigned int mask)
+{
+ struct pipeline *p = pipelines + pp;
+ p->sop = op;
+ p->sref = ref;
+ p->smask = mask;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_blend(int pp, int enable)
+{
+ struct pipeline *p = pipelines + pp;
+ p->blend = enable;
+ p->vkobj_valid = 0;
+}
+
+void vk_pipeln_blendfunc(int pp, int src, int dst)
+{
+ struct pipeline *p = pipelines + pp;
+ p->srcblend = src;
+ p->dstblend = dst;
+ p->vkobj_valid = 0;
+}
+
+VkPipeline vk_pipeln(int pp)
+{
+ int i, idx, num_sdr;
+ struct pipeline *p = pipelines + pp;
+ VkPipelineShaderStageCreateInfo ssinf[VKSDR_MAX];
+ VkPipelineVertexInputStateCreateInfo vinp;
+ VkPipelineInputAssemblyStateCreateInfo vasm;
+ VkPipelineViewportStateCreateInfo vp;
+ VkPipelineRasterizationStateCreateInfo rast;
+ VkPipelineMultisampleStateCreateInfo msaa;
+ VkPipelineDepthStencilStateCreateInfo zst;
+ VkPipelineColorBlendAttachmentState bat;
+ VkPipelineColorBlendStateCreateInfo blend;
+ VkPipelineLayoutCreateInfo lay;
+ VkGraphicsPipelineCreateInfo pinf;
+
+
+ if(p->vkobj_valid) {
+ return p->vkobj;
+ }
+
+ if(p->vkobj) {
+ vkDestroyPipeline(vkdev, p->vkobj, 0);
+ p->vkobj = 0;
+ }
+
+ memset(ssinf, 0, sizeof ssinf);
+ idx = 0;
+ for(i=0; i<VKSDR_MAX; i++) {
+ if(p->sdr[idx]) {
+ ssinf[idx].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ ssinf[idx].stage = VKSDR_STAGE(i);
+ ssinf[idx].module = p->sdr[idx];
+ ssinf[idx].pName = "main";
+ idx++;
+ }
+ }
+ num_sdr = idx;
+
+ memset(&vinp, 0, sizeof vinp);
+ vinp.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+
+ memset(&vasm, 0, sizeof vasm);
+ vasm.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ vasm.topology = p->prim;
+
+ memset(&vp, 0, sizeof vp);
+ vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ vp.viewportCount = 1;
+ vp.pViewports = &p->vport;
+ vp.scissorCount = 1;
+ vp.pScissors = &p->scissor;
+
+ memset(&rast, 0, sizeof rast);
+ rast.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rast.polygonMode = p->polymode;
+ rast.lineWidth = p->line_width;
+ rast.cullMode = p->cull;
+ rast.frontFace = p->frontface;
+
+ /* TODO */
+ memset(&msaa, 0, sizeof msaa);
+ msaa.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ msaa.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ msaa.minSampleShading = 1.0f;
+
+ memset(&zst, 0, sizeof zst);
+ zst.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+ zst.depthTestEnable = p->zbuf;
+ zst.depthWriteEnable = p->depthwr;
+ zst.depthCompareOp = p->zop;
+ zst.stencilTestEnable = p->stencil;
+ zst.front.writeMask = p->stencilwr;
+ zst.front.failOp = p->sfail;
+ zst.front.passOp = p->szpass;
+ zst.front.depthFailOp = p->szfail;
+ zst.front.compareOp = p->sop;
+ zst.front.compareMask = p->smask;
+ zst.front.reference = p->sref;
+ zst.back = zst.front;
+
+ memset(&bat, 0, sizeof bat);
+ bat.colorWriteMask = p->colorwr;
+ bat.blendEnable = p->blend;
+ bat.srcColorBlendFactor = p->srcblend;
+ bat.dstColorBlendFactor = p->dstblend;
+ bat.colorBlendOp = VK_BLEND_OP_ADD; /* TODO */
+ bat.srcAlphaBlendFactor = p->srcblend_a;
+ bat.dstAlphaBlendFactor = p->dstblend_a;
+
+ memset(&blend, 0, sizeof blend);
+ blend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ blend.attachmentCount = 1;
+ blend.pAttachments = &bat;
+
+ /* TODO */
+ memset(&lay, 0, sizeof lay);
+ lay.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ if(vkCreatePipelineLayout(vkdev, &lay, 0, &p->vkobj_layout) != 0) {
+ return 0;
+ }
+
+ memset(&pinf, 0, sizeof pinf);
+ pinf.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ pinf.stageCount = num_sdr;
+ pinf.pStages = ssinf;
+ pinf.pVertexInputState = &vinp;
+ pinf.pInputAssemblyState = &vasm;
+ pinf.pViewportState = &vp;
+ pinf.pRasterizationState = &rast;
+ pinf.pDepthStencilState = &zst;
+ pinf.pMultisampleState = &msaa;
+ pinf.pColorBlendState = &blend;
+ pinf.layout = p->vkobj_layout;
+ pinf.renderPass = p->rpass;
+ pinf.basePipelineIndex = -1;
+
+ if(vkCreateGraphicsPipelines(vkdev, 0, 1, &pinf, 0, &p->vkobj) != 0) {
+ return 0;
+ }
+ p->vkobj_valid = 1;
+ return p->vkobj;
+}
+
+
+VkShaderModule vk_load_shader(const char *fname)
+{
+ FILE *fp;
+ long sz;
+ void *buf;
+ VkShaderModuleCreateInfo sinf;
+ VkShaderModule sdr;
+
+ if(!(fp = fopen(fname, "rb"))) {
+ fprintf(stderr, "failed to open shader: %s: %s\n", fname, strerror(errno));
+ return 0;
+ }
+ fseek(fp, 0, SEEK_END);
+ sz = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ buf = alloca(sz);
+ if(fread(buf, 1, sz, fp) < sz) {
+ fprintf(stderr, "unexpected EOF while reading shader: %s\n", fname);
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+
+ memset(&sinf, 0, sizeof sinf);
+ sinf.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ sinf.codeSize = sz;
+ sinf.pCode = buf;
+
+ if(vkCreateShaderModule(vkdev, &sinf, 0, &sdr) != 0) {
+ fprintf(stderr, "failed to create shader from %s\n", fname);
+ return 0;
+ }
+ return sdr;
+}
+
+void vk_free_shader(VkShaderModule sdr)
+{
+ vkDestroyShaderModule(vkdev, sdr, 0);
+}
+
+VkSemaphore vk_create_sem(void)
+{
+ VkSemaphore sem;
+ VkSemaphoreCreateInfo sinf = {0};
+
+ sinf.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ if(vkCreateSemaphore(vkdev, &sinf, 0, &sem) != 0) {
+ return 0;
+ }
+ return sem;
+}
+
+void vk_free_sem(VkSemaphore sem)
+{
+ vkDestroySemaphore(vkdev, sem, 0);
+}
+
+
+void vk_rect(VkRect2D *r, int x, int y, int w, int h)
+{
+ r->offset.x = x;
+ r->offset.y = y;
+ r->extent.width = w;
+ r->extent.height = h;
+}
+
+
+#define ARRSZ(arr) (sizeof arr / sizeof *arr)
+static const char *known_layer_list[] = {
+ "VK_LAYER_KHRONOS_validation",
+ "VK_LAYER_GOOGLE_threading",
+ "VK_LAYER_LUNARG_parameter_validation",
+ "VK_LAYER_LUNARG_object_tracker",
+ "VK_LAYER_LUNARG_image",
+ "VK_LAYER_LUNARG_core_validation",
+ "VK_LAYER_LUNARG_swapchain",
+ "VK_LAYER_GOOGLE_unique_objects"
+};
+
+static struct {
+ const char *name;
+ int required;
+} known_instext_list[] = {
+ {"VK_KHR_surface", 1},
+#ifdef _WIN32
+ {"VK_KHR_win32_surface", 1},
+#else
+ /*{"VK_KHR_xlib_surface", 1},*/
+ {"VK_KHR_xcb_surface", 1},
+#endif
+ {"VK_KHR_debug_report", 0}
+};
+
+static struct {
+ const char *name;
+ int required;
+} known_devext_list[] = {
+ {"VK_KHR_swapchain", 1},
+ {"VK_KHR_acceleration_structure", 0},
+ {"VK_KHR_deferred_host_operations", 0},
+ {"VK_KHR_ray_tracing_pipeline", 0}
+};
+
+static int create_instance(void)
+{
+ int i, nlayers = 0, next = 0;
+ VkInstanceCreateInfo instinf;
+ VkApplicationInfo appinf;
+ const char *layers[ARRSZ(known_layer_list)];
+ const char *ext[ARRSZ(known_instext_list)];
+ uint32_t apiver;
+
+ vkEnumerateInstanceVersion(&apiver);
+ printf("Vulkan API version: %d.%d.%d\n", (apiver >> 22) & 0x7f,
+ (apiver >> 12) & 0x3ff, apiver & 0xfff);
+
+ memset(&appinf, 0, sizeof appinf);
+ appinf.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+ appinf.pApplicationName = "vkray";
+ appinf.pEngineName = "vkray";
+ appinf.apiVersion = apiver;
+
+ vkEnumerateInstanceLayerProperties(&inst_layers_count, 0);
+ inst_layers = malloc_nf(inst_layers_count * sizeof *inst_layers);
+ vkEnumerateInstanceLayerProperties(&inst_layers_count, inst_layers);
+
+ vkEnumerateInstanceExtensionProperties(0, &inst_ext_count, 0);
+ inst_ext = malloc_nf(inst_ext_count * sizeof *inst_ext);
+ vkEnumerateInstanceExtensionProperties(0, &inst_ext_count, inst_ext);
+
+ printf("Layers:\n");
+ for(i=0; i<inst_layers_count; i++) {
+ printf(" - %s: %s\n", inst_layers[i].layerName, inst_layers[i].description);
+ }
+ printf("Instance extensions:\n");
+ for(i=0; i<inst_ext_count; i++) {
+ printf(" - %s\n", inst_ext[i].extensionName);
+ }
+
+ have_debug_report = have_ext(inst_ext, inst_ext_count, "VK_KHR_debug_report");
+
+ for(i=0; i<ARRSZ(known_layer_list); i++) {
+ if(have_inst_layer(known_layer_list[i])) {
+ layers[nlayers++] = known_layer_list[i];
+ }
+ }
+ for(i=0; i<ARRSZ(known_instext_list); i++) {
+ if(have_ext(inst_ext, inst_ext_count, known_instext_list[i].name)) {
+ ext[next++] = known_instext_list[i].name;
+ } else if(known_instext_list[i].required) {
+ fprintf(stderr, "Vulkan implementation lacks required instance extension: %s\n",
+ known_instext_list[i].name);
+ return -1;
+ }
+ }
+
+ memset(&instinf, 0, sizeof instinf);
+ instinf.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ instinf.pApplicationInfo = &appinf;
+ instinf.enabledLayerCount = nlayers;
+ instinf.ppEnabledLayerNames = layers;
+ instinf.enabledExtensionCount = next;
+ instinf.ppEnabledExtensionNames = ext;
+ if(vkCreateInstance(&instinf, 0, &vk) != 0) {
+ fprintf(stderr, "failed to create vulkan instance\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int create_surface(void)
+{
+#ifdef _WIN32
+ VkWin32SurfaceCreateInfoKHR winf = {0};
+ winf.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
+ winf.hinstance = hinst;
+ winf.hwnd = win;
+
+ if(vkCreateWin32SurfaceKHR(vk, &winf, 0, &vksurf) != 0) {
+ fprintf(stderr, "failed to create win32 window surface\n");
+ return -1;
+ }
+#else
+ /*
+ VkXlibSurfaceCreateInfoKHR xinf = {0};
+ xinf.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
+ xinf.dpy = dpy;
+ xinf.window = win;
+
+ if(vkCreateXlibSurfaceKHR(vk, &xinf, 0, &vksurf) != 0) {
+ fprintf(stderr, "failed to create Xlib window surface\n");
+ return -1;
+ }
+ */
+ VkXcbSurfaceCreateInfoKHR xinf = {0};
+ xinf.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
+ xinf.connection = XGetXCBConnection(dpy);
+ xinf.window = (xcb_window_t)win;
+
+ if(vkCreateXcbSurfaceKHR(vk, &xinf, 0, &vksurf) != 0) {
+ fprintf(stderr, "failed to create XCB window surface\n");
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int choose_phys_dev(void)
+{
+ uint32_t i, num_pdev, score, best_score, best_dev;
+ VkPhysicalDevice *pdev;
+ VkPhysicalDeviceProperties pdevprop;
+ VkBool32 can_pres;
+
+ vkEnumeratePhysicalDevices(vk, &num_pdev, 0);
+ if(!num_pdev) {
+ fprintf(stderr, "no vulkan devices found\n");
+ return -1;
+ }
+ pdev = malloc_nf(num_pdev * sizeof *pdev);
+ vkEnumeratePhysicalDevices(vk, &num_pdev, pdev);
+
+ printf("Found %d physical devices\n", num_pdev);
+
+ best_score = 0;
+ best_dev = -1;
+ for(i=0; i<num_pdev; i++) {
+ if((score = eval_pdev_score(pdev[i])) && score > best_score) {
+ best_score = score;
+ best_dev = i;
+ }
+
+ vkGetPhysicalDeviceProperties(pdev[i], &pdevprop);
+ printf(" %d: %s (score: %d)\n", i, pdevprop.deviceName, score);
+ }
+ if(best_dev == -1) {
+ fprintf(stderr, "no suitable vulkan device found\n");
+ free(pdev);
+ return -1;
+ }
+ vkpdev = pdev[best_dev];
+
+ if(qfam) free(qfam);
+
+ vkGetPhysicalDeviceQueueFamilyProperties(vkpdev, &num_qfam, 0);
+ qfam = malloc_nf(num_qfam * sizeof *qfam);
+ vkGetPhysicalDeviceQueueFamilyProperties(vkpdev, &num_qfam, qfam);
+
+ free(pdev);
+ choose_pixfmt();
+ return 0;
+}
+
+static int create_device(void)
+{
+ float *prio;
+ VkDeviceQueueCreateInfo qinf[MAX_INIT_QUEUE] = {0};
+ VkPhysicalDeviceFeatures feat = {0};
+ VkDeviceCreateInfo devinf = {0};
+ const char *ext[ARRSZ(known_devext_list) + 16];
+ int i, j, num_ext, qfam, totalq;
+
+ vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, 0);
+ dev_ext = malloc_nf(dev_ext_count * sizeof *dev_ext);
+ vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, dev_ext);
+
+ num_ext = 0;
+ for(i=0; i<ARRSZ(known_devext_list); i++) {
+ if(have_ext(dev_ext, dev_ext_count, known_devext_list[i].name)) {
+ ext[num_ext++] = known_devext_list[i].name;
+ } else if(known_devext_list[i].required) {
+ fprintf(stderr, "Vulkan device lacks required extension: %s\n",
+ known_devext_list[i].name);
+ return -1;
+ }
+ }
+
+ if(initflags & VKINIT_RAY) {
+ if(have_ext(dev_ext, dev_ext_count, "VK_KHR_acceleration_structure") &&
+ have_ext(dev_ext, dev_ext_count, "VK_KHR_deferred_host_operations") &&
+ have_ext(dev_ext, dev_ext_count, "VK_KHR_ray_tracing_pipeline")) {
+ ext[num_ext++] = "VK_KHR_acceleration_structure";
+ ext[num_ext++] = "VK_KHR_deferred_host_operations";
+ ext[num_ext++] = "VK_KHR_ray_tracing_pipeline";
+ } else {
+ initflags &= ~VKINIT_RAY;
+ }
+ }
+
+ totalq = 0;
+ for(i=0; i<num_initq; i++) {
+ totalq += initq[i].count;
+ }
+ if(totalq > 1024) {
+ fprintf(stderr, "create_device: arbitrary limit of total queues exceeded (%d)\n", totalq);
+ return -1;
+ }
+ prio = alloca(totalq * sizeof *prio);
+
+ for(i=0; i<num_initq; i++) {
+ if((qfam = vk_find_qfamily(initq[i].flags)) == -1) {
+ fprintf(stderr, "create_device: failed to find queue family (flags: 0x%2x)\n",
+ initq[i].flags);
+ return -1;
+ }
+ initq[i].qfam = qfam;
+ initq[i].cmdpool = 0;
+
+ qinf[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ qinf[i].queueFamilyIndex = qfam;
+ qinf[i].queueCount = initq[i].count;
+ qinf[i].pQueuePriorities = prio;
+ for(j=0; j<initq[i].count; i++) {
+ *prio++ = 1.0f; /* all queue priorities 1 */
+ }
+ }
+
+ devinf.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+ devinf.pQueueCreateInfos = qinf;
+ devinf.queueCreateInfoCount = num_initq;
+ devinf.pEnabledFeatures = &feat;
+ devinf.enabledExtensionCount = num_ext;
+ devinf.ppEnabledExtensionNames = ext;
+
+ if(vkCreateDevice(vkpdev, &devinf, 0, &vkdev) != 0) {
+ fprintf(stderr, "failed to create vulkan device\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int create_swapchain(void)
+{
+ int i;
+ uint32_t num;
+ VkSwapchainCreateInfoKHR scinf = {0};
+ VkImageViewCreateInfo ivinf;
+
+ if(vksc_extent.width <= 0 || vksc_extent.height <= 0) {
+ return -1;
+ }
+
+ scinf.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+ scinf.surface = vksurf;
+ scinf.minImageCount = 2;
+ scinf.imageFormat = vksurf_fmt[vksurf_selfmt].format;
+ scinf.imageColorSpace = vksurf_fmt[vksurf_selfmt].colorSpace;
+ scinf.imageExtent = vksc_extent;
+ scinf.imageArrayLayers = 1;
+ scinf.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+ scinf.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ scinf.preTransform = vksurf_caps.currentTransform;
+ scinf.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+ scinf.presentMode = VK_PRESENT_MODE_FIFO_KHR;
+ scinf.clipped = VK_TRUE;
+
+ if(vkCreateSwapchainKHR(vkdev, &scinf, 0, &vksc) != 0) {
+ fprintf(stderr, "failed to create swapchain\n");
+ return -1;
+ }
+
+ if(!vksc_img) {
+ free(vksc_img);
+ vkGetSwapchainImagesKHR(vkdev, vksc, &num, 0);
+ vksc_img = malloc_nf(num * sizeof *vksc_img);
+ vkGetSwapchainImagesKHR(vkdev, vksc, &num, vksc_img);
+ }
+ if(!vksc_view || vksc_numimg != num) {
+ free(vksc_view);
+ vksc_view = malloc_nf(num * sizeof *vksc_view);
+ }
+ vksc_numimg = num;
+
+ for(i=0; i<num; i++) {
+ memset(&ivinf, 0, sizeof ivinf);
+ ivinf.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ ivinf.image = vksc_img[i];
+ ivinf.format = vksurf_fmt[vksurf_selfmt].format;
+ ivinf.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+ ivinf.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+ ivinf.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+ ivinf.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+ ivinf.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ ivinf.subresourceRange.levelCount = 1;
+ ivinf.subresourceRange.layerCount = 1;
+ ivinf.viewType = VK_IMAGE_VIEW_TYPE_2D;
+
+ if(vkCreateImageView(vkdev, &ivinf, 0, vksc_view + i) != 0) {
+ fprintf(stderr, "failed to create image view (%d)\n", i);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int create_default_cmdbuf(void)
+{
+ int i, qfam;
+ VkCommandPool cmdpool;
+ VkCommandBufferAllocateInfo cbinf = {0};
+
+ assert(!vksc_cmdbuf);
+
+ if((qfam = vk_find_qfamily(VKQ_GFX | VKQ_PRESENT)) == -1) {
+ fprintf(stderr, "failed to find a gfx|present capable queue family\n");
+ return -1;
+ }
+ if(!(cmdpool = find_cmdpool(qfam))) {
+ fprintf(stderr, "failed to find usable command pool for default command buffers\n");
+ return -1;
+ }
+ if(!(vksc_cmdbuf = malloc(vksc_numimg * sizeof *vksc_cmdbuf))) {
+ fprintf(stderr, "failed to allocate %d command buffers for the swap-chain\n", vksc_numimg);
+ return -1;
+ }
+
+ cbinf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ cbinf.commandPool = cmdpool;
+ cbinf.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ cbinf.commandBufferCount = vksc_numimg;
+
+ if(vkAllocateCommandBuffers(vkdev, &cbinf, vksc_cmdbuf) != 0) {
+ fprintf(stderr, "failed to create %d command buffers for the swap-chain\n", vksc_numimg);
+ return -1;
+ }
+ return 0;
+}
+
+static int eval_pdev_score(VkPhysicalDevice dev)
+{
+ int score = 0;
+ uint32_t i, num_fmt, num_qfam, num_ext;
+ VkQueueFamilyProperties *qfam;
+ VkExtensionProperties *ext;
+ VkPhysicalDeviceProperties prop;
+ VkPhysicalDeviceFeatures feat;
+ VkSurfaceFormatKHR *sfmt;
+ VkBool32 can_pres;
+
+ vkGetPhysicalDeviceProperties(dev, &prop);
+ vkGetPhysicalDeviceFeatures(dev, &feat);
+
+ /* check if we have the swapchain extension */
+ vkEnumerateDeviceExtensionProperties(dev, 0, &num_ext, 0);
+ ext = malloc_nf(num_ext * sizeof *ext);
+ vkEnumerateDeviceExtensionProperties(dev, 0, &num_ext, ext);
+
+ if(!have_ext(ext, num_ext, "VK_KHR_swapchain")) {
+ free(ext);
+ return 0;
+ }
+
+ /* populate format and present modes arrays, and make sure we have some of each */
+ vkGetPhysicalDeviceSurfaceFormatsKHR(dev, vksurf, &num_fmt, 0);
+ if(!num_fmt) {
+ free(ext);
+ return 0;
+ }
+ sfmt = malloc_nf(num_fmt * sizeof *sfmt);
+ vkGetPhysicalDeviceSurfaceFormatsKHR(dev, vksurf, &num_fmt, sfmt);
+
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, vksurf, &vksurf_caps);
+
+ /* find a queue family which can do graphics and can present */
+ vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, 0);
+ qfam = malloc_nf(num_qfam * sizeof *qfam);
+ vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, qfam);
+
+ for(i=0; i<num_qfam; i++) {
+ vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, vksurf, &can_pres);
+ if(qfam[i].queueCount && (qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && can_pres) {
+ score = 1;
+ }
+ }
+
+ switch(prop.deviceType) {
+ case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
+ score++;
+ break;
+ case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
+ score += 2;
+ break;
+ case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
+ score += 4;
+ break;
+ default:
+ break;
+ }
+
+ if(initflags & VKINIT_RAY) {
+ if(have_ext(ext, num_ext, "VK_KHR_acceleration_structure") &&
+ have_ext(ext, num_ext, "VK_KHR_ray_tracing_pipeline")) {
+ score += 100;
+ }
+ }
+
+ free(ext);
+ free(sfmt);
+ free(qfam);
+ return score;
+}
+
+static int choose_pixfmt(void)
+{
+ static const VkFormat pref[] = {
+ VK_FORMAT_B8G8R8_UNORM,
+ VK_FORMAT_R8G8B8_UNORM,
+ VK_FORMAT_B8G8R8A8_UNORM,
+ VK_FORMAT_R8G8B8A8_UNORM
+ };
+ int i, j;
+ uint32_t num_fmt;
+
+ vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, 0);
+ if(!num_fmt) return -1;
+ vksurf_fmt = malloc_nf(num_fmt * sizeof *vksurf_fmt);
+ vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, vksurf_fmt);
+
+ vksurf_selfmt = 0;
+ for(i=0; i<num_fmt; i++) {
+ if(vksurf_fmt[i].colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
+ continue;
+ }
+ for(j=0; j<sizeof pref / sizeof *pref; j++) {
+ if(vksurf_fmt[i].format == pref[j]) {
+ vksurf_selfmt = i;
+ vksurf_numfmt = num_fmt;
+ return i;
+ }
+ }
+ }
+ free(vksurf_fmt);
+ vksurf_fmt = 0;
+ return -1;
+}
+
+
+static int have_inst_layer(const char *name)
+{
+ int i;
+ for(i=0; i<inst_layers_count; i++) {
+ if(strcmp(inst_layers[i].layerName, name) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int have_ext(VkExtensionProperties *ext, int next, const char *name)
+{
+ int i;
+ for(i=0; i<next; i++) {
+ if(strcmp(ext[i].extensionName, name) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}