initial commit
authorJohn Tsiombikas <nuclear@member.fsf.org>
Fri, 12 Aug 2022 03:55:37 +0000 (06:55 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Fri, 12 Aug 2022 03:55:37 +0000 (06:55 +0300)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
src/app.c [new file with mode: 0644]
src/app.h [new file with mode: 0644]
src/darray.c [new file with mode: 0644]
src/darray.h [new file with mode: 0644]
src/main_x11.c [new file with mode: 0644]
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]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..537ac62
--- /dev/null
@@ -0,0 +1,4 @@
+*.o
+*.swp
+*.d
+vktest
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..8eb23e7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,27 @@
+src = $(wildcard src/*.c)
+obj = $(src:.c=.o)
+dep = $(src:.c=.d)
+bin = vktest
+
+sdr = $(wildcard sdr/*.glsl)
+spirv = $(sdr:.glsl=.spv)
+
+warn = -pedantic -Wall -Wno-unused-variable
+
+CFLAGS = $(warn) -g -MMD
+LDFLAGS = -lvulkan -lX11 -lX11-xcb
+
+$(bin): $(obj) $(spirv)
+       $(CC) -o $@ $(obj) $(LDFLAGS)
+
+%.spv: %.v.glsl
+       glslangValidator -o $@ -S vert -V $<
+
+%.spv: %.p.glsl
+       glslangValidator -o $@ -S frag -V $<
+
+-include $(dep)
+
+.PHONY: clean
+clean:
+       rm -f $(bin) $(obj) $(spirv)
diff --git a/src/app.c b/src/app.c
new file mode 100644 (file)
index 0000000..044a885
--- /dev/null
+++ b/src/app.c
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "app.h"
+#include "vk.h"
+
+int app_init(void)
+{
+       unsigned int flags;
+
+       if(vk_init(VKINIT_DEPTH, &flags) == -1) {
+               return -1;
+       }
+       return 0;
+}
+
+void app_cleanup(void)
+{
+       vk_cleanup();
+}
+
+
+void app_display(void)
+{
+}
+
+void app_reshape(int x, int y)
+{
+       if(vk_reshape(x, y) == -1) {
+               abort();
+       }
+}
+
+void app_keyboard(int key, int press)
+{
+       if(!press) return;
+
+       switch(key) {
+       case 27:
+               app_quit();
+               break;
+       }
+}
+
+void app_mouse(int bn, int press, int x, int y)
+{
+}
+
+void app_motion(int x, int y)
+{
+}
diff --git a/src/app.h b/src/app.h
new file mode 100644 (file)
index 0000000..4f891e7
--- /dev/null
+++ b/src/app.h
@@ -0,0 +1,22 @@
+#ifndef APP_H_
+#define APP_H_
+
+enum {
+       KEY_DEL = 127,
+       KEY_HOME, KEY_END,
+       KEY_PGUP, KEY_PGDOWN
+};
+
+int app_init(void);
+void app_cleanup(void);
+
+void app_display(void);
+void app_reshape(int x, int y);
+void app_keyboard(int key, int press);
+void app_mouse(int bn, int press, int x, int y);
+void app_motion(int x, int y);
+
+void app_quit(void);
+void app_swap_buffers(void);
+
+#endif /* APP_H_ */
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_ */
diff --git a/src/main_x11.c b/src/main_x11.c
new file mode 100644 (file)
index 0000000..6ff15b8
--- /dev/null
@@ -0,0 +1,173 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "app.h"
+#include "vk.h"
+
+static Window create_window(const char *title, int xsz, int ysz);
+static void handle_event(XEvent *ev);
+static int translate_keysym(KeySym sym);
+
+static Display *dpy;
+static Window win, root_win;
+static int xscr;
+static Atom xa_wm_proto, xa_wm_del_win;
+static int reshape_pending, quit;
+static int win_width, win_height, win_mapped;
+
+int main(int argc, char **argv)
+{
+       XEvent ev;
+
+       if(!(dpy = XOpenDisplay(0))) {
+               fprintf(stderr, "failed to connect to the X server\n");
+               return 1;
+       }
+       xscr = DefaultScreen(dpy);
+       root_win = RootWindow(dpy, xscr);
+       xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
+       xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+
+       if(!(win = create_window("vkray", 1280, 800))) {
+               return 1;
+       }
+       vk_init_xwin(dpy, win);
+
+       if(app_init() == -1) {
+               goto end;
+       }
+
+       for(;;) {
+               while(XPending(dpy)) {
+                       XNextEvent(dpy, &ev);
+                       handle_event(&ev);
+                       if(quit) goto end;
+               }
+
+               if(reshape_pending) {
+                       app_reshape(win_width, win_height);
+               }
+               app_display();
+       }
+
+end:
+       app_cleanup();
+       XDestroyWindow(dpy, win);
+       XCloseDisplay(dpy);
+       return 0;
+}
+
+void app_quit(void)
+{
+       quit = 1;
+}
+
+void app_swap_buffers(void)
+{
+}
+
+static Window create_window(const char *title, int xsz, int ysz)
+{
+       Window win;
+       XVisualInfo vinf;
+       XTextProperty txprop;
+       XSetWindowAttributes xattr;
+
+       if(!XMatchVisualInfo(dpy, xscr, 24, TrueColor, &vinf)) {
+               fprintf(stderr, "no suitable visual found\n");
+               return 0;
+       }
+
+       xattr.background_pixel = BlackPixel(dpy, xscr);
+       xattr.colormap = XCreateColormap(dpy, root_win, vinf.visual, AllocNone);
+
+       if(!(win = XCreateWindow(dpy, root_win, 0, 0, xsz, ysz, 0, 24, InputOutput,
+                                       vinf.visual, CWBackPixel | CWColormap, &xattr))) {
+               fprintf(stderr, "failed to create window\n");
+               return 0;
+       }
+
+       XSelectInput(dpy, win, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
+                       ButtonMotionMask | ExposureMask | StructureNotifyMask);
+
+       XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
+
+       if(XStringListToTextProperty((char**)&title, 1, &txprop)) {
+               XSetWMName(dpy, win, &txprop);
+               XSetWMIconName(dpy, win, &txprop);
+               XFree(txprop.value);
+       }
+
+       XMapWindow(dpy, win);
+
+       reshape_pending = 1;
+       return win;
+}
+
+static void handle_event(XEvent *ev)
+{
+       KeySym sym;
+       int key;
+
+       switch(ev->type) {
+       case MapNotify:
+               win_mapped = 1;
+               break;
+       case UnmapNotify:
+               win_mapped = 0;
+               break;
+
+       case ConfigureNotify:
+               if(ev->xconfigure.width != win_width || ev->xconfigure.height != win_height) {
+                       win_width = ev->xconfigure.width;
+                       win_height = ev->xconfigure.height;
+                       reshape_pending = 1;
+               }
+               break;
+
+       case KeyPress:
+       case KeyRelease:
+               if((sym = XLookupKeysym(&ev->xkey, 0)) && (key = translate_keysym(sym))) {
+                       app_keyboard(key, ev->type == KeyPress);
+               }
+               break;
+
+       case ButtonPress:
+       case ButtonRelease:
+               app_mouse(ev->xbutton.button - Button1, ev->type == ButtonPress, ev->xbutton.x, ev->xbutton.y);
+               break;
+
+       case ClientMessage:
+               if(ev->xclient.message_type == xa_wm_proto) {
+                       if(ev->xclient.data.l[0] == xa_wm_del_win) {
+                               quit = 1;
+                       }
+               }
+               break;
+
+       default:
+               break;
+       }
+}
+
+static int translate_keysym(KeySym sym)
+{
+       if(sym < 128) return sym;
+
+       switch(sym) {
+       case XK_Escape: return 27;
+       case XK_Tab: return '\t';
+       case XK_Return: return '\n';
+       case XK_BackSpace: return '\b';
+       case XK_Delete: return KEY_DEL;
+       case XK_Home: return KEY_HOME;
+       case XK_End: return KEY_END;
+       case XK_Page_Up: return KEY_PGUP;
+       case XK_Page_Down: return KEY_PGDOWN;
+       default:
+               break;
+       }
+
+       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..61ae532
--- /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..44c838f
--- /dev/null
+++ b/src/vk.c
@@ -0,0 +1,690 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <vulkan/vulkan.h>
+#include "vk.h"
+#include "util.h"
+
+#ifdef __WIN32__
+#include <vulkan/vulkan_win32.h>
+#else
+/*#include <vulkan/vulkan_xlib.h>*/
+#include <X11/Xlib-xcb.h>
+#include <vulkan/vulkan_xcb.h>
+#endif
+
+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 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);
+
+static Display *dpy;
+static Window win;
+static int initflags;
+
+static VkInstance vk;
+static VkPhysicalDevice vkpdev;
+static int vkqfam_idx, vkqfam_maxq;
+static VkDevice vkdev;
+static VkQueue vkq;
+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 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;
+
+void vk_init_xwin(Display *d, Window w)
+{
+       dpy = d;
+       win = w;
+}
+
+int vk_init(unsigned int flags, unsigned int *usedflags)
+{
+       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) {
+               if(usedflags) {
+                       *usedflags = initflags;
+               } else {
+                       vk_cleanup();
+                       return -1;
+               }
+       }
+       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;
+
+       /* 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 vkobj_valid;
+       VkRenderPass vkobj;
+};
+
+static struct rpass *rpasses;
+
+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->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;
+}
+
+void vk_rpass_clear(int clear)
+{
+       rpasses[rp].clear = clear;
+}
+
+VkRenderPass vk_rpass(int rp)
+{
+       struct rpass *r;
+       VkAttachmentDescription cat;
+       VkAttachmentReference catref[16];
+       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;
+
+               for(i=0; i<r->num_colbuf; i++) {
+                       memset(&catref[i], 0, sizeof catref);
+                       catref[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+               }
+
+               memset(&subpass, 0, sizeof subpass);
+               subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+               subpass.colorAttachmentCount = r->num_colbuf;
+               subpass.pColorAttachments = catref;
+
+               memset(&dep, 0, sizeof dep);
+               dep.srcSubpass = 
+       }
+
+       return rpasses[rp].vkobj;
+}
+
+#define ARRSZ(arr)     (sizeof arr / sizeof *arr)
+static const char *known_layer_list[] = {
+       "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_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)
+{
+       /*
+       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;
+       }
+       return 0;
+}
+
+int choose_phys_dev(void)
+{
+       uint32_t i, num_pdev, num_qfam, score, best_score, best_dev;
+       VkPhysicalDevice *pdev;
+       VkPhysicalDeviceProperties pdevprop;
+       VkQueueFamilyProperties *qfam;
+       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];
+
+       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;
+}
+
+static int create_device(void)
+{
+       float prio = 1.0f;
+       VkDeviceQueueCreateInfo qinf = {0};
+       VkPhysicalDeviceFeatures feat = {0};
+       VkDeviceCreateInfo devinf = {0};
+       const char *ext[ARRSZ(known_devext_list) + 16];
+       int i, num_ext;
+
+       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_ray_tracing_pipeline")) {
+                       ext[num_ext++] = "VK_KHR_acceleration_structure";
+                       ext[num_ext++] = "VK_KHR_ray_tracing_pipeline";
+               } else {
+                       initflags &= ~VKINIT_RAY;
+               }
+       }
+
+       qinf.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+       qinf.queueFamilyIndex = vkqfam_idx;
+       qinf.queueCount = 1;
+       qinf.pQueuePriorities = &prio;
+
+       devinf.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+       devinf.pQueueCreateInfos = &qinf;
+       devinf.queueCreateInfoCount = 1;
+       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;
+       }
+
+       vkGetDeviceQueue(vkdev, vkqfam_idx, 0, &vkq);
+       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 || vksc_numimg != num) {
+               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 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..4e94784
--- /dev/null
+++ b/src/vk.h
@@ -0,0 +1,26 @@
+#ifndef VK_H_
+#define VK_H_
+
+#include <X11/Xlib.h>
+
+enum {
+       VKINIT_DEPTH    = 1,
+       VKINIT_STENCIL  = 2,
+       VKINIT_RAY              = 0x100
+};
+
+void vk_init_xwin(Display *dpy, Window win);
+
+int vk_init(unsigned int flags, unsigned int *usedflags);
+void vk_cleanup(void);
+
+int vk_reshape(int xsz, int ysz);
+
+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);
+VkRenderPass vk_rpass(int rp);
+
+
+#endif /* VK_H_ */