From: John Tsiombikas Date: Fri, 12 Aug 2022 03:55:37 +0000 (+0300) Subject: initial commit X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=vktest3;a=commitdiff_plain;h=ddea0efd29740987177c52d89833c378f6a88b88 initial commit --- ddea0efd29740987177c52d89833c378f6a88b88 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..537ac62 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o +*.swp +*.d +vktest diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..044a885 --- /dev/null +++ b/src/app.c @@ -0,0 +1,50 @@ +#include +#include +#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 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 index 0000000..66c0715 --- /dev/null +++ b/src/darray.c @@ -0,0 +1,122 @@ +#include +#include +#include +#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 index 0000000..b9a7051 --- /dev/null +++ b/src/darray.h @@ -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 index 0000000..6ff15b8 --- /dev/null +++ b/src/main_x11.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#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 index 0000000..dc86d10 --- /dev/null +++ b/src/util.c @@ -0,0 +1,43 @@ +#include +#include +#include +#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 index 0000000..61ae532 --- /dev/null +++ b/src/util.h @@ -0,0 +1,31 @@ +#ifndef UTIL_H_ +#define UTIL_H_ + +#include +#include + +#if defined(__WATCOMC__) || defined(WIN32) +#include +#else +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) +#include +#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 index 0000000..44c838f --- /dev/null +++ b/src/vk.c @@ -0,0 +1,690 @@ +#include +#include +#include +#include +#include "vk.h" +#include "util.h" + +#ifdef __WIN32__ +#include +#else +/*#include */ +#include +#include +#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; iused = 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; inum_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 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 + +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_ */