pulled in vk.h/vk.c + dependencies from vktest3 and added missing win32 ifdefs
authorJohn Tsiombikas <nuclear@mutantstargoat.com>
Sat, 31 Aug 2024 18:15:53 +0000 (21:15 +0300)
committerJohn Tsiombikas <nuclear@mutantstargoat.com>
Sat, 31 Aug 2024 18:15:53 +0000 (21:15 +0300)
12 files changed:
.gitignore
src/darray.c [new file with mode: 0644]
src/darray.h [new file with mode: 0644]
src/main.c
src/util.c [new file with mode: 0644]
src/util.h [new file with mode: 0644]
src/vk.c [new file with mode: 0644]
src/vk.h [new file with mode: 0644]
src/winsys.h
src/winsys_w32.c
vkraytest.vcxproj
vkraytest.vcxproj.filters

index 59e1e31..5746161 100644 (file)
@@ -5,3 +5,4 @@ Debug/
 Release/
 x64/
 *.vcxproj.user
+.vs/
diff --git a/src/darray.c b/src/darray.c
new file mode 100644 (file)
index 0000000..66c0715
--- /dev/null
@@ -0,0 +1,122 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "darray.h"
+#include "util.h"
+
+
+/* The array descriptor keeps auxilliary information needed to manipulate
+ * the dynamic array. It's allocated adjacent to the array buffer.
+ */
+struct arrdesc {
+       int nelem, szelem;
+       int max_elem;
+       int bufsz;      /* not including the descriptor */
+};
+
+#define DESC(x)                ((struct arrdesc*)((char*)(x) - sizeof(struct arrdesc)))
+
+void *darr_alloc(int elem, int szelem)
+{
+       struct arrdesc *desc;
+
+       desc = malloc_nf(elem * szelem + sizeof *desc);
+       desc->nelem = desc->max_elem = elem;
+       desc->szelem = szelem;
+       desc->bufsz = elem * szelem;
+       return (char*)desc + sizeof *desc;
+}
+
+void darr_free(void *da)
+{
+       if(da) {
+               free(DESC(da));
+       }
+}
+
+void *darr_resize_impl(void *da, int elem)
+{
+       int newsz;
+       struct arrdesc *desc;
+
+       if(!da) return 0;
+       desc = DESC(da);
+
+       newsz = desc->szelem * elem;
+       desc = realloc_nf(desc, newsz + sizeof *desc);
+
+       desc->nelem = desc->max_elem = elem;
+       desc->bufsz = newsz;
+       return (char*)desc + sizeof *desc;
+}
+
+int darr_empty(void *da)
+{
+       return DESC(da)->nelem ? 0 : 1;
+}
+
+int darr_size(void *da)
+{
+       return DESC(da)->nelem;
+}
+
+
+void *darr_clear_impl(void *da)
+{
+       return darr_resize_impl(da, 0);
+}
+
+/* stack semantics */
+void *darr_push_impl(void *da, void *item)
+{
+       struct arrdesc *desc;
+       int nelem;
+
+       desc = DESC(da);
+       nelem = desc->nelem;
+
+       if(nelem >= desc->max_elem) {
+               /* need to resize */
+               int newsz = desc->max_elem ? desc->max_elem * 2 : 1;
+
+               da = darr_resize_impl(da, newsz);
+               desc = DESC(da);
+               desc->nelem = nelem;
+       }
+
+       if(item) {
+               memcpy((char*)da + desc->nelem * desc->szelem, item, desc->szelem);
+       }
+       desc->nelem++;
+       return da;
+}
+
+void *darr_pop_impl(void *da)
+{
+       struct arrdesc *desc;
+       int nelem;
+
+       desc = DESC(da);
+       nelem = desc->nelem;
+
+       if(!nelem) return da;
+
+       if(nelem <= desc->max_elem / 3) {
+               /* reclaim space */
+               int newsz = desc->max_elem / 2;
+
+               da = darr_resize_impl(da, newsz);
+               desc = DESC(da);
+               desc->nelem = nelem;
+       }
+       desc->nelem--;
+
+       return da;
+}
+
+void *darr_finalize(void *da)
+{
+       struct arrdesc *desc = DESC(da);
+       memmove(desc, da, desc->bufsz);
+       return desc;
+}
diff --git a/src/darray.h b/src/darray.h
new file mode 100644 (file)
index 0000000..b9a7051
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef DYNAMIC_ARRAY_H_
+#define DYNAMIC_ARRAY_H_
+
+void *darr_alloc(int elem, int szelem);
+void darr_free(void *da);
+void *darr_resize_impl(void *da, int elem);
+#define darr_resize(da, elem)  do { (da) = darr_resize_impl(da, elem); } while(0)
+
+int darr_empty(void *da);
+int darr_size(void *da);
+
+void *darr_clear_impl(void *da);
+#define darr_clear(da)                 do { (da) = darr_clear_impl(da); } while(0)
+
+/* stack semantics */
+void *darr_push_impl(void *da, void *item);
+#define darr_push(da, item)            do { (da) = darr_push_impl(da, item); } while(0)
+void *darr_pop_impl(void *da);
+#define darr_pop(da)                   do { (da) = darr_pop_impl(da); } while(0)
+
+/* Finalize the array. No more resizing is possible after this call.
+ * Use free() instead of dynarr_free() to deallocate a finalized array.
+ * Returns pointer to the finalized array.
+ * Complexity: O(n)
+ */
+void *darr_finalize(void *da);
+
+/* utility macros to push characters to a string. assumes and maintains
+ * the invariant that the last element is always a zero
+ */
+#define darr_strpush(da, c) \
+       do { \
+               char cnull = 0, ch = (char)(c); \
+               (da) = dynarr_pop_impl(da); \
+               (da) = dynarr_push_impl((da), &ch); \
+               (da) = dynarr_push_impl((da), &cnull); \
+       } while(0)
+
+#define darr_strpop(da) \
+       do { \
+               char cnull = 0; \
+               (da) = dynarr_pop_impl(da); \
+               (da) = dynarr_pop_impl(da); \
+               (da) = dynarr_push_impl((da), &cnull); \
+       } while(0)
+
+
+#endif /* DYNAMIC_ARRAY_H_ */
index 3ba70f7..0d26c4f 100644 (file)
@@ -1,6 +1,6 @@
 #include <stdio.h>
-#include <vulkan/vulkan.h>
 #include "winsys.h"
+#include "vk.h"
 
 static int win_width, win_height;
 
@@ -10,6 +10,16 @@ int main(int argc, char **argv)
                return 1;
        }
 
+#ifdef _WIN32
+       vk_init_win(hinst, win);
+#else
+       vk_init_x11(dpy, win);
+#endif
+       if(vk_init(VKINIT_RAY, 0) == -1) {
+               destroy_window();
+               return 1;
+       }
+
        for(;;) {
                handle_events();
                if(quit) break;
@@ -17,6 +27,7 @@ int main(int argc, char **argv)
                display();
        }
 
+       vk_cleanup();
        destroy_window();
        return 0;
 }
diff --git a/src/util.c b/src/util.c
new file mode 100644 (file)
index 0000000..dc86d10
--- /dev/null
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "util.h"
+
+void *malloc_nf_impl(size_t sz, const char *file, int line)
+{
+       void *p;
+       if(!(p = malloc(sz))) {
+               fprintf(stderr, "%s:%d failed to allocate %lu bytes\n", file, line, (unsigned long)sz);
+               abort();
+       }
+       return p;
+}
+
+void *calloc_nf_impl(size_t num, size_t sz, const char *file, int line)
+{
+       void *p;
+       if(!(p = calloc(num, sz))) {
+               fprintf(stderr, "%s:%d failed to allocate %lu bytes\n", file, line, (unsigned long)(sz * num));
+               abort();
+       }
+       return p;
+}
+
+void *realloc_nf_impl(void *p, size_t sz, const char *file, int line)
+{
+       if(!(p = realloc(p, sz))) {
+               fprintf(stderr, "%s:%d failed to realloc %lu bytes\n", file, line, (unsigned long)sz);
+               abort();
+       }
+       return p;
+}
+
+char *strdup_nf_impl(const char *s, const char *file, int line)
+{
+       char *res;
+       if(!(res = strdup(s))) {
+               fprintf(stderr, "%s:%d failed to duplicate string\n", file, line);
+               abort();
+       }
+       return res;
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644 (file)
index 0000000..61f6d47
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(__WATCOMC__) || defined(_WIN32)
+#include <malloc.h>
+#else
+#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
+#include <alloca.h>
+#endif
+#endif
+
+#ifdef _MSC_VER
+#define strcasecmp(s, k) stricmp(s, k)
+#endif
+
+#define malloc_nf(sz)  malloc_nf_impl(sz, __FILE__, __LINE__)
+void *malloc_nf_impl(size_t sz, const char *file, int line);
+
+#define calloc_nf(num, sz)     calloc_nf_impl(num, sz, __FILE__, __LINE__)
+void *calloc_nf_impl(size_t num, size_t sz, const char *file, int line);
+
+#define realloc_nf(p, sz)      realloc_nf_impl(p, sz, __FILE__, __LINE__)
+void *realloc_nf_impl(void *p, size_t sz, const char *file, int line);
+
+#define strdup_nf(s)   strdup_nf_impl(s, __FILE__, __LINE__)
+char *strdup_nf_impl(const char *s, const char *file, int line);
+
+#endif /* UTIL_H_ */
diff --git a/src/vk.c b/src/vk.c
new file mode 100644 (file)
index 0000000..a18a0cd
--- /dev/null
+++ b/src/vk.c
@@ -0,0 +1,1590 @@
+#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;
+}
diff --git a/src/vk.h b/src/vk.h
new file mode 100644 (file)
index 0000000..accc10c
--- /dev/null
+++ b/src/vk.h
@@ -0,0 +1,134 @@
+#ifndef VK_H_
+#define VK_H_
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <X11/Xlib.h>
+#endif
+#include <vulkan/vulkan.h>
+
+enum {
+       VKINIT_DEPTH    = 1,
+       VKINIT_STENCIL  = 2,
+       VKINIT_RAY              = 0x100
+};
+
+/* queue capability flags for vk_find_qfamily and vk_init_queue */
+enum {
+       VKQ_GFX                 = 1,
+       VKQ_COMPUTE             = 2,
+       VKQ_PRESENT             = 4,
+       VKQ_XFER                = 8
+};
+
+/* shader types
+ * arranged so that: (1 << VKSDR_FOO) == VK_SHADER_STAGE_FOO_BIT
+ */
+enum {
+       VKSDR_VERTEX    = 0,
+       VKSDR_TESS_CTL  = 1,
+       VKSDR_TESS_EVAL = 2,
+       VKSDR_GEOM              = 3,
+       VKSDR_PIXEL             = 4,
+       VKSDR_COMPUTE   = 5,
+
+       VKSDR_TASK              = 6,
+       VKSDR_MESH              = 7,
+
+       VKSDR_RAYGEN    = 8,
+       VKSDR_ANYHIT    = 9,
+       VKSDR_CLOSESTHIT = 10,
+       VKSDR_MISS              = 11,
+       VKSDR_ISECT             = 12,
+
+       VKSDR_MAX
+};
+#define VKSDR_STAGE(x) (1 << (x))
+
+/* primitives */
+enum {
+       VKPRIM_POINTS           = VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
+       VKPRIM_LINES            = VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
+       VKPRIM_TRIANGLES        = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
+};
+
+#ifdef _WIN32
+void vk_init_win(HINSTANCE hinst, HWND win);
+#else
+void vk_init_xwin(Display *dpy, Window win);
+#endif
+void vk_init_queue(unsigned int qflags, int count);
+
+int vk_init(unsigned int flags, unsigned int *usedflags);
+void vk_cleanup(void);
+
+int vk_reshape(int xsz, int ysz);
+
+/* returns the number of swapchain images */
+int vk_num_swap_images(void);
+VkImageView vk_swap_image(int idx);
+/* returns the image index, or -1 on failure. Pass optional semaphore to signal */
+int vk_next_swap_image(VkSemaphore sem);
+
+int vk_submit(VkQueue q, VkCommandBuffer cmdbuf, VkSemaphore semwait, VkSemaphore semsig);
+int vk_present(VkQueue q, int imgid, VkSemaphore semwait);
+
+int vk_find_qfamily(unsigned int flags);
+VkQueue vk_getq_fam(int fam, int n);
+VkQueue vk_getq(unsigned int flags, int n);
+
+VkCommandBuffer vk_create_cmdbuf_fam(int qfam, int level);
+VkCommandBuffer vk_create_cmdbuf(unsigned int qflags, int level);
+/* return one of the swap-chain command buffers */
+VkCommandBuffer vk_get_cmdbuf(int imgid);
+
+int vk_create_rpass(void);
+void vk_free_rpass(int rp);
+void vk_rpass_colorbuf(int rp, int fmt, int n);
+void vk_rpass_msaa(int rp, int nsamp);
+void vk_rpass_clear(int rp, int clear);
+VkRenderPass vk_rpass(int rp);
+
+int vk_create_fb(void);
+void vk_free_fb(int fb);
+void vk_fb_size(int fb, int x, int y);
+void vk_fb_rpass(int fb, int rpass);
+void vk_fb_images(int fb, int n, ...);
+VkFramebuffer vk_fb(int fb);
+
+int vk_create_pipeln(void);
+void vk_free_pipeln(int pp);
+void vk_pipeln_rpass(int pp, VkRenderPass rp);
+void vk_pipeln_viewport(int pp, int x, int y, int width, int height);
+void vk_pipeln_scissor(int pp, int x, int y, int width, int height);
+void vk_pipeln_shader(int pp, int type, VkShaderModule sdr);
+/* TODO: vertex input */
+void vk_pipeln_prim(int pp, int prim);
+void vk_pipeln_polymode(int pp, int mode);
+void vk_pipeln_cull(int pp, int cull);
+void vk_pipeln_frontface(int pp, int ff);
+void vk_pipeln_linewidth(int pp, int w);
+void vk_pipeln_multisample(int pp, int nsamples);
+void vk_pipeln_colormask(int pp, int r, int g, int b, int a);
+void vk_pipeln_depthmask(int pp, int z);
+void vk_pipeln_stencilmask(int pp, int s);
+void vk_pipeln_zbuffer(int pp, int enable);
+void vk_pipeln_zbuffer_op(int pp, int op);
+void vk_pipeln_stencil(int pp, int enable);
+void vk_pipeln_stencil_op(int pp, int sfail, int zfail, int zpass);
+void vk_pipeln_stencil_func(int pp, int op, unsigned int ref, unsigned int mask);
+void vk_pipeln_blend(int pp, int enable);
+void vk_pipeln_blendfunc(int pp, int src, int dst);
+VkPipeline vk_pipeln(int pp);
+
+VkShaderModule vk_load_shader(const char *fname);
+void vk_free_shader(VkShaderModule sdr);
+
+VkSemaphore vk_create_sem(void);
+void vk_free_sem(VkSemaphore sem);
+
+/* random helpers */
+void vk_rect(VkRect2D *r, int x, int y, int w, int h);
+
+#endif /* VK_H_ */
index 28fa015..5f61b1c 100644 (file)
@@ -12,6 +12,7 @@ extern Window win;
 #define WSYS_WIN32
 #include <windows.h>
 
+extern HINSTANCE hinst;
 extern HWND win;
 #else
 #error "unknown window system"
index 7098c7f..01a6f05 100644 (file)
@@ -1,13 +1,13 @@
 #include <stdio.h>
 #include "winsys.h"
 
+HINSTANCE hinst;
 HWND win;
 int quit;
 
 static int win_width, win_height, mapped;
 
 static int init_done, reshape_pending;
-static HINSTANCE hinst;
 static HDC dc;
 static WNDCLASSEX wc;
 
index c5b0238..4446472 100644 (file)
       <SDLCheck>true</SDLCheck>
       <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>true</ConformanceMode>
-      <DisableSpecificWarnings>4244</DisableSpecificWarnings>
+      <DisableSpecificWarnings>4244;4996</DisableSpecificWarnings>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>$(CoreLibraryDependencies);%(AdditionalDependencies);vulkan-1.lib</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\VulkanSDK\1.3.290.0\Lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
       <SDLCheck>true</SDLCheck>
       <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>true</ConformanceMode>
-      <DisableSpecificWarnings>4244</DisableSpecificWarnings>
+      <DisableSpecificWarnings>4244;4996</DisableSpecificWarnings>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>$(CoreLibraryDependencies);%(AdditionalDependencies);vulkan-1.lib</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\VulkanSDK\1.3.290.0\Lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
       <SDLCheck>true</SDLCheck>
       <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>true</ConformanceMode>
-      <DisableSpecificWarnings>4244</DisableSpecificWarnings>
+      <DisableSpecificWarnings>4244;4996</DisableSpecificWarnings>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>$(CoreLibraryDependencies);%(AdditionalDependencies);vulkan-1.lib</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\VulkanSDK\1.3.290.0\Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
       <SDLCheck>true</SDLCheck>
       <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>true</ConformanceMode>
-      <DisableSpecificWarnings>4244</DisableSpecificWarnings>
+      <DisableSpecificWarnings>4244;4996</DisableSpecificWarnings>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>$(CoreLibraryDependencies);%(AdditionalDependencies);vulkan-1.lib</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\VulkanSDK\1.3.290.0\Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="src\darray.c" />
     <ClCompile Include="src\main.c" />
+    <ClCompile Include="src\util.c" />
+    <ClCompile Include="src\vk.c" />
     <ClCompile Include="src\winsys_w32.c" />
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="src\darray.h" />
+    <ClInclude Include="src\util.h" />
+    <ClInclude Include="src\vk.h" />
     <ClInclude Include="src\winsys.h" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
index ea7f6f4..d984dcf 100644 (file)
     <ClCompile Include="src\winsys_w32.c">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\vk.c">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\darray.c">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\util.c">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\winsys.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\vk.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\darray.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\util.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>
\ No newline at end of file