foo
[vktest3] / src / vk.c
index 44c838f..ebc31ae 100644 (file)
--- a/src/vk.c
+++ b/src/vk.c
@@ -1,9 +1,13 @@
 #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 <vulkan/vulkan_win32.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);
@@ -27,12 +93,20 @@ static int have_ext(VkExtensionProperties *ext, int next, const char *name);
 static Display *dpy;
 static Window win;
 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 int vkqfam_idx, vkqfam_maxq;
+static VkQueueFamilyProperties *qfam;
+static uint32_t num_qfam;
 static VkDevice vkdev;
-static VkQueue vkq;
 static VkSurfaceKHR vksurf;
 static VkSurfaceCapabilitiesKHR vksurf_caps;
 static int vksurf_numfmt, vksurf_selfmt;
@@ -42,6 +116,7 @@ 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;
@@ -58,8 +133,32 @@ void vk_init_xwin(Display *d, Window w)
        win = w;
 }
 
+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;
@@ -137,23 +236,128 @@ int vk_reshape(int xsz, int 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;
 }
 
-struct rpass {
-       int used;
-       int fmt;
-       int num_colbuf;
-       int num_samples;
-       /* TODO: stuff about depth-stencil */
-       int clear;
+int vk_next_image(VkSemaphore sem)
+{
+       uint32_t idx;
+       if(vkAcquireNextImageKHR(vkdev, vksc, UINT64_MAX, sem, 0, &idx) != 0) {
+               return -1;
+       }
+       return (int)idx;
+}
 
-       int vkobj_valid;
-       VkRenderPass vkobj;
-};
+int vk_find_qfamily(unsigned int flags)
+{
+       int i, famidx = -1;
+       VkBool32 can_pres;
 
-static struct rpass *rpasses;
+       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);
+}
 
 int vk_create_rpass(void)
 {
@@ -174,6 +378,7 @@ int vk_create_rpass(void)
        /* 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;
@@ -209,50 +414,596 @@ void vk_rpass_colorbuf(int rp, int fmt, int n)
 void vk_rpass_msaa(int rp, int nsamp)
 {
        rpasses[rp].num_samples = nsamp;
+       rpasses[rp].vkobj_valid = 0;
 }
 
-void vk_rpass_clear(int clear)
+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 cat;
-       VkAttachmentReference catref[16];
+       VkAttachmentDescription att[17];
+       VkAttachmentReference catref[16], zatref;
        VkSubpassDescription subpass;
        VkRenderPassCreateInfo pinf;
-       VkSubpassDependency dep;
 
        r = rpasses + rp;
 
        if(!r->vkobj_valid) {
-               memset(&cat, 0, sizeof cat);
-               cat.format = r->fmt;
-               cat.samples = r->num_samples;
-               cat.loadOp = r->clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-               cat.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
-               cat.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-               cat.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-               cat.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-               cat.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+               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++) {
-                       memset(&catref[i], 0, sizeof catref);
+                       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 = &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 rpass */
+       }
+
+       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)
+{
+       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;
+
+               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;
+               }
+       }
 
-               memset(&dep, 0, sizeof dep);
-               dep.srcSubpass = 
+       /* 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;
+}
 
-       return rpasses[rp].vkobj;
+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);
 }
 
 #define ARRSZ(arr)     (sizeof arr / sizeof *arr)
@@ -384,10 +1135,9 @@ static int create_surface(void)
 
 int choose_phys_dev(void)
 {
-       uint32_t i, num_pdev, num_qfam, score, best_score, best_dev;
+       uint32_t i, num_pdev, score, best_score, best_dev;
        VkPhysicalDevice *pdev;
        VkPhysicalDeviceProperties pdevprop;
-       VkQueueFamilyProperties *qfam;
        VkBool32 can_pres;
 
        vkEnumeratePhysicalDevices(vk, &num_pdev, 0);
@@ -418,21 +1168,12 @@ int choose_phys_dev(void)
        }
        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);
 
-       vkqfam_idx = -1;
-       for(i=0; i<num_qfam; i++) {
-               vkGetPhysicalDeviceSurfaceSupportKHR(vkpdev, i, vksurf, &can_pres);
-               if(qfam[i].queueCount && (qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && can_pres) {
-                       vkqfam_maxq = qfam[i].queueCount;
-                       vkqfam_idx = i;
-                       break;
-               }
-       }
-
-       free(qfam);
        free(pdev);
        choose_pixfmt();
        return 0;
@@ -440,12 +1181,12 @@ int choose_phys_dev(void)
 
 static int create_device(void)
 {
-       float prio = 1.0f;
-       VkDeviceQueueCreateInfo qinf = {0};
+       float *prio;
+       VkDeviceQueueCreateInfo qinf[MAX_INIT_QUEUE] = {0};
        VkPhysicalDeviceFeatures feat = {0};
        VkDeviceCreateInfo devinf = {0};
        const char *ext[ARRSZ(known_devext_list) + 16];
-       int i, num_ext;
+       int i, j, num_ext, qfam, totalq;
 
        vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, 0);
        dev_ext = malloc_nf(dev_ext_count * sizeof *dev_ext);
@@ -472,14 +1213,37 @@ static int create_device(void)
                }
        }
 
-       qinf.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
-       qinf.queueFamilyIndex = vkqfam_idx;
-       qinf.queueCount = 1;
-       qinf.pQueuePriorities = &prio;
+       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 = 1;
+       devinf.pQueueCreateInfos = qinf;
+       devinf.queueCreateInfoCount = num_initq;
        devinf.pEnabledFeatures = &feat;
        devinf.enabledExtensionCount = num_ext;
        devinf.ppEnabledExtensionNames = ext;
@@ -488,8 +1252,6 @@ static int create_device(void)
                fprintf(stderr, "failed to create vulkan device\n");
                return -1;
        }
-
-       vkGetDeviceQueue(vkdev, vkqfam_idx, 0, &vkq);
        return 0;
 }
 
@@ -557,6 +1319,38 @@ static int create_swapchain(void)
        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)
 {