vertex buffers master
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 21 Sep 2024 20:59:48 +0000 (23:59 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 21 Sep 2024 20:59:48 +0000 (23:59 +0300)
sdr/sdr.v.glsl
src/app.c
src/vk.c
src/vk.h

index 3a60cbe..10807fa 100644 (file)
@@ -5,22 +5,13 @@ out gl_PerVertex {
        vec4 gl_Position;
 };
 
-layout(location = 0) out vec3 color;
-
-vec2 vpos[3] = vec2[](
-       vec2(0.0, -0.5),
-       vec2(0.5, 0.5),
-       vec2(-0.5, 0.5)
-);
+layout(location = 0) in vec4 attr_vertex;
+layout(location = 1) in vec4 attr_color;
 
-vec3 vcol[3] = vec3[](
-       vec3(1.0, 0.0, 0.0),
-       vec3(0.0, 1.0, 0.0),
-       vec3(0.0, 0.0, 1.0)
-);
+layout(location = 0) out vec3 color;
 
 void main()
 {
-       gl_Position = vec4(vpos[gl_VertexIndex], 0.0, 1.0);
-       color = vcol[gl_VertexIndex];
+       gl_Position = attr_vertex;
+       color = attr_color.rgb;
 }
index 41779d6..cd50cf6 100644 (file)
--- a/src/app.c
+++ b/src/app.c
@@ -22,11 +22,27 @@ static VkSemaphore sem_draw;
 static VkQueue queue;
 static VkShaderModule vsdr, psdr;
 
+static float verts[] = {
+       0, -0.5, 0, 1,
+       0.5, 0.5, 0, 1,
+       -0.5, 0.5, 0, 1
+};
+
+static float colors[] = {
+       1, 0, 0, 1,
+       0, 1, 0, 1,
+       0, 0, 1, 1
+};
+
+static int vbpos, vbcol;
+
 int app_init(void)
 {
        int i, qfam;
        unsigned int flags;
        VkCommandPool cmdpool;
+       VkVertexInputBindingDescription vinpdesc[2];
+       VkVertexInputAttributeDescription vattrdesc[2];
 
        if(vk_init(VKINIT_DEPTH, &flags) == -1) {
                return -1;
@@ -85,6 +101,16 @@ int app_init(void)
        vk_pipeln_zbuffer(pipeln, 0);
        vk_pipeln_frontface(pipeln, VK_FRONT_FACE_CLOCKWISE);
 
+       vk_pipeln_vattr(pipeln, 0, 0, 0, 0, VKATTR_VEC4);
+       vk_pipeln_vattr(pipeln, 1, 1, 0, 0, VKATTR_VEC4);
+
+       vbpos = vk_create_buf(VKBUF_VERTEX);
+       vk_buf_size(vbpos, sizeof verts);
+       vk_buf_data(vbpos, 0, sizeof verts, verts);
+       vbcol = vk_create_buf(VKBUF_VERTEX);
+       vk_buf_size(vbcol, sizeof colors);
+       vk_buf_data(vbcol, 0, sizeof colors, colors);
+
        sem_draw = vk_create_sem();
        return 0;
 }
@@ -95,6 +121,9 @@ void app_cleanup(void)
 
        vk_finish();
 
+       vk_free_buf(vbpos);
+       vk_free_buf(vbcol);
+
        for(i=0; i<num_swap_img; i++) {
                vk_free_fb(fb[i]);
        }
@@ -114,7 +143,7 @@ void app_cleanup(void)
 
 void app_display(void)
 {
-       int imgid;
+       int i, imgid;
        VkCommandBuffer cmdbuf;
        struct frame *frm;
 
@@ -124,6 +153,7 @@ void app_display(void)
        imgid = vk_next_swap_image(frm->sem_getimg);
        cmdbuf = frm->cmdbuf;
 
+       /* record the command buffer: begin-rpass, bind-pipeline, draw, end-rpass */
        {
                VkCommandBufferBeginInfo cmdbegin = {0};
                VkRenderPassBeginInfo rpbegin = {0};
@@ -152,7 +182,8 @@ void app_display(void)
                rpbegin.clearValueCount = 1;
 
                vkCmdBeginRenderPass(cmdbuf, &rpbegin, VK_SUBPASS_CONTENTS_INLINE);
-               vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, vk_pipeln(pipeln));
+               vkcmd_bind_gfxpipeln(cmdbuf, pipeln);
+               vkcmd_bind_vbuf(cmdbuf, 2, vbpos, vbcol);
                vkCmdDraw(cmdbuf, 3, 1, 0, 0);
                vkCmdEndRenderPass(cmdbuf);
                vkEndCommandBuffer(cmdbuf);
index c6c5c4b..0895ee6 100644 (file)
--- a/src/vk.c
+++ b/src/vk.c
 
 #ifdef _WIN32
 #include <windows.h>
+#include <malloc.h>
 #include <vulkan/vulkan_win32.h>
 #else
+#include <alloca.h>
 /*#include <vulkan/vulkan_xlib.h>*/
 #include <X11/Xlib-xcb.h>
 #include <vulkan/vulkan_xcb.h>
@@ -46,11 +48,18 @@ struct framebuf {
        VkFramebuffer vkobj;
 };
 
+struct vertex_attrib {
+       int used;
+       VkVertexInputBindingDescription bind;
+       VkVertexInputAttributeDescription attr;
+};
+
 struct pipeline {
        int used;
        VkViewport vport;
        VkRect2D scissor;
        VkShaderModule sdr[VKSDR_MAX];
+       struct vertex_attrib vattr[VKATTR_MAX];
        VkPrimitiveTopology prim;
        VkPolygonMode polymode;
        float line_width;
@@ -73,10 +82,20 @@ struct pipeline {
        VkPipelineLayout vkobj_layout;  /* TODO probably best to split this */
 };
 
+struct buffer {
+       int used;
+       unsigned int size;
+       VkBufferUsageFlags usage;
+       VkBuffer vkobj;
+       int vkobj_valid;
+       VkDeviceMemory devmem;
+};
+
 
 static struct rpass *rpasses;
 static struct framebuf *framebufs;
 static struct pipeline *pipelines;
+static struct buffer *buffers;
 
 
 static int create_instance(void);
@@ -112,6 +131,7 @@ static VkInstance vk;
 static VkPhysicalDevice vkpdev;
 static VkQueueFamilyProperties *qfam;
 static uint32_t num_qfam;
+static VkPhysicalDeviceMemoryProperties vkmemprop;
 static VkDevice vkdev;
 static VkSurfaceKHR vksurf;
 static VkSurfaceCapabilitiesKHR vksurf_caps;
@@ -483,6 +503,7 @@ void vk_free_rpass(int rp)
 
        if(rpasses[rp].used && rpasses[rp].vkobj) {
                vkDestroyRenderPass(vkdev, rpasses[rp].vkobj, 0);
+               rpasses[rp].vkobj = 0;
        }
        rpasses[rp].used = 0;
 }
@@ -802,7 +823,57 @@ void vk_pipeln_shader(int pp, int type, VkShaderModule sdr)
        p->vkobj_valid = 0;
 }
 
-/* TODO: vertex input */
+void vk_pipeln_vattr(int pp, int loc, int bind, int offs, int stride, int fmt)
+{
+       int i, idx = -1;
+       struct pipeline *p = pipelines + pp;
+       struct vertex_attrib *attr;
+
+       for(i=0; i<VKATTR_MAX; i++) {
+               attr = p->vattr + i;
+               if(attr->used) {
+                       if(attr->attr.location == loc) {
+                               idx = i;
+                               break;
+                       }
+               } else {
+                       if(idx == -1) {
+                               idx = i;
+                       }
+               }
+       }
+
+       if(idx == -1) return;
+
+       attr = p->vattr + idx;
+       attr->used = 1;
+       attr->attr.location = loc;
+       attr->attr.binding = attr->bind.binding = bind;
+       attr->attr.offset = offs;
+       switch(fmt) {
+       case VKATTR_FLOAT:
+               attr->attr.format = VK_FORMAT_R32_SFLOAT;
+               if(stride <= 0) stride = sizeof(float);
+               break;
+       case VKATTR_VEC2:
+               attr->attr.format = VK_FORMAT_R32G32_SFLOAT;
+               if(stride <= 0) stride = 2 * sizeof(float);
+               break;
+       case VKATTR_VEC3:
+               attr->attr.format = VK_FORMAT_R32G32B32_SFLOAT;
+               if(stride <= 0) stride = 3 * sizeof(float);
+               break;
+       case VKATTR_VEC4:
+               attr->attr.format = VK_FORMAT_R32G32B32A32_SFLOAT;
+               if(stride <= 0) stride = 4 * sizeof(float);
+               break;
+       default:
+               break;
+       }
+       attr->bind.stride = stride;
+       attr->bind.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;     /* TODO support instancing */
+}
+
 void vk_pipeln_prim(int pp, int prim)
 {
        struct pipeline *p = pipelines + pp;
@@ -924,7 +995,7 @@ void vk_pipeln_blendfunc(int pp, int src, int dst)
 
 VkPipeline vk_pipeln(int pp)
 {
-       int i, idx, num_sdr;
+       int i, idx, num_sdr, num_vattr;
        struct pipeline *p = pipelines + pp;
        VkPipelineShaderStageCreateInfo ssinf[VKSDR_MAX];
        VkPipelineVertexInputStateCreateInfo vinp;
@@ -937,6 +1008,8 @@ VkPipeline vk_pipeln(int pp)
        VkPipelineColorBlendStateCreateInfo blend;
        VkPipelineLayoutCreateInfo lay;
        VkGraphicsPipelineCreateInfo pinf;
+       VkVertexInputBindingDescription bdesc[VKATTR_MAX];
+       VkVertexInputAttributeDescription adesc[VKATTR_MAX];
 
 
        if(p->vkobj_valid) {
@@ -961,8 +1034,21 @@ VkPipeline vk_pipeln(int pp)
        }
        num_sdr = idx;
 
+       num_vattr = 0;
+       for(i=0; i<VKATTR_MAX; i++) {
+               if(p->vattr[i].used) {
+                       bdesc[num_vattr] = p->vattr[i].bind;
+                       adesc[num_vattr] = p->vattr[i].attr;
+                       num_vattr++;
+               }
+       }
+
        memset(&vinp, 0, sizeof vinp);
        vinp.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+       vinp.pVertexBindingDescriptions = bdesc;
+       vinp.vertexBindingDescriptionCount = num_vattr;
+       vinp.pVertexAttributeDescriptions = adesc;
+       vinp.vertexAttributeDescriptionCount = num_vattr;
 
        memset(&vasm, 0, sizeof vasm);
        vasm.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
@@ -1046,6 +1132,139 @@ VkPipeline vk_pipeln(int pp)
        return p->vkobj;
 }
 
+int vk_create_buf(unsigned int type)
+{
+       int i;
+       struct buffer buf = {0}, *b = &buf;
+
+       if(!buffers) {
+               buffers = darr_alloc(0, sizeof *buffers);
+               darr_push(buffers, &buf);       /* add dummy buffer */
+       }
+
+       for(i=1; i<darr_size(buffers); i++) {
+               if(!buffers[i].used) {
+                       b = buffers + i;
+               }
+       }
+
+       /* init buffer defaults */
+       memset(b, 0, sizeof *b);
+       b->used = 1;
+       b->usage = (VkBufferUsageFlags)type;
+
+       if(b == &buf) {
+               darr_push(buffers, b);
+               return darr_size(buffers) - 1;
+       }
+       return b - buffers;
+}
+
+void vk_free_buf(int bb)
+{
+       if(!buffers || bb < 1 || bb >= darr_size(buffers)) {
+               return;
+       }
+
+       if(buffers[bb].used && buffers[bb].vkobj) {
+               if(buffers[bb].devmem) {
+                       vkFreeMemory(vkdev, buffers[bb].devmem, 0);
+                       buffers[bb].devmem = 0;
+               }
+               vkDestroyBuffer(vkdev, buffers[bb].vkobj, 0);
+               buffers[bb].vkobj = 0;
+       }
+       buffers[bb].used = 0;
+}
+
+void vk_buf_usage(int bb, unsigned int type)
+{
+       struct buffer *b = buffers + bb;
+       b->usage = type;
+       b->vkobj_valid = 0;
+}
+
+void vk_buf_size(int bb, unsigned int size)
+{
+       struct buffer *b = buffers + bb;
+       b->size = size;
+       b->vkobj_valid = 0;
+}
+
+static int find_memtype(unsigned int mask, VkMemoryPropertyFlagBits flags)
+{
+       int i;
+       for(i=0; i<vkmemprop.memoryTypeCount; i++) {
+               if((mask & (1 << i)) && (flags & vkmemprop.memoryTypes[i].propertyFlags) == flags) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+VkBuffer vk_buf(int bb)
+{
+       struct buffer *b = buffers + bb;
+       VkBufferCreateInfo binf;
+       VkMemoryRequirements mreq;
+       VkMemoryAllocateInfo alloc;
+
+       if(!b->vkobj_valid) {
+               if(b->vkobj) {
+                       vkDestroyBuffer(vkdev, b->vkobj, 0);
+                       b->vkobj = 0;
+               }
+
+               memset(&binf, 0, sizeof binf);
+               binf.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+               binf.usage = b->usage;
+               binf.size = b->size;
+               binf.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+               if(vkCreateBuffer(vkdev, &binf, 0, &b->vkobj) != 0) {
+                       fprintf(stderr, "failed to create buffer\n");
+                       return 0;
+               }
+
+               vkGetBufferMemoryRequirements(vkdev, b->vkobj, &mreq);
+
+               memset(&alloc, 0, sizeof alloc);
+               alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+               alloc.allocationSize = mreq.size;
+               alloc.memoryTypeIndex = find_memtype(mreq.memoryTypeBits, VKMEM_HOSTVIS | VKMEM_HOSTCOH);
+               if(vkAllocateMemory(vkdev, &alloc, 0, &b->devmem) != 0) {
+                       fprintf(stderr, "failed to allocate %ld bytes of device memory\n", mreq.size);
+                       vkDestroyBuffer(vkdev, b->vkobj, 0);
+                       b->vkobj = 0;
+                       return 0;
+               }
+               vkBindBufferMemory(vkdev, b->vkobj, b->devmem, 0);
+
+               b->vkobj_valid = 1;
+       }
+
+       return b->vkobj;
+}
+
+void *vk_buf_map(int bb, int offs, int sz)
+{
+       void *ptr;
+       vk_buf(bb);             /* force lazy construction completion */
+       vkMapMemory(vkdev, buffers[bb].devmem, offs, sz, 0, &ptr);
+       return ptr;
+}
+
+void vk_buf_unmap(int bb)
+{
+       vkUnmapMemory(vkdev, buffers[bb].devmem);
+}
+
+void vk_buf_data(int bb, int offs, int sz, void *data)
+{
+       void *ptr = vk_buf_map(bb, offs, sz);
+       memcpy(ptr, data, sz);
+       vk_buf_unmap(bb);
+}
 
 VkShaderModule vk_load_shader(const char *fname)
 {
@@ -1131,6 +1350,32 @@ void vk_wait_fence(VkFence fence, long timeout)
        vkResetFences(vkdev, 1, &fence);
 }
 
+void vkcmd_bind_gfxpipeln(VkCommandBuffer cmdbuf, int pp)
+{
+       vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, vk_pipeln(pp));
+}
+
+void vkcmd_bind_vbuf(VkCommandBuffer cmdbuf, int count, ...)
+{
+       int i, bb;
+       va_list ap;
+       VkBuffer *barr;
+       VkDeviceSize *offsarr;
+
+       barr = alloca(count * sizeof *barr);
+       offsarr = alloca(count * sizeof *offsarr);
+
+       va_start(ap, count);
+       for(i=0; i<count; i++) {
+               bb = va_arg(ap, int);
+               barr[i] = vk_buf(bb);
+               offsarr[i] = 0;
+       }
+       va_end(ap);
+
+       vkCmdBindVertexBuffers(cmdbuf, 0, count, barr, offsarr);
+}
+
 void vk_rect(VkRect2D *r, int x, int y, int w, int h)
 {
        r->offset.x = x;
@@ -1413,6 +1658,9 @@ static int create_device(void)
                devinf.pNext = init_rayext_list();
        }
 
+       /* retreive memory properties for the selected device */
+       vkGetPhysicalDeviceMemoryProperties(vkpdev, &vkmemprop);
+
        if(vkCreateDevice(vkpdev, &devinf, 0, &vkdev) != 0) {
                fprintf(stderr, "failed to create vulkan device\n");
                return -1;
index e987e96..18d1de0 100644 (file)
--- a/src/vk.h
+++ b/src/vk.h
@@ -46,6 +46,15 @@ enum {
 };
 #define VKSDR_STAGE(x) (1 << (x))
 
+#define VKATTR_MAX             8
+
+enum {
+       VKATTR_FLOAT,
+       VKATTR_VEC2,
+       VKATTR_VEC3,
+       VKATTR_VEC4
+};
+
 /* primitives */
 enum {
        VKPRIM_POINTS           = VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
@@ -53,6 +62,23 @@ enum {
        VKPRIM_TRIANGLES        = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
 };
 
+/* buffer types */
+enum {
+       VKBUF_VERTEX            = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+       VKBUF_INDEX                     = VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
+       VKBUF_UNIFORM           = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
+};
+
+/* memory type bits */
+enum {
+       VKMEM_DEVLOCAL          = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+       VKMEM_HOSTVIS           = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
+       VKMEM_HOSTCOH           = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+       VKMEM_HOSTCACHED        = VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
+       VKMEM_LAZY                      = VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT,
+       VKMEM_PROT                      = VK_MEMORY_PROPERTY_PROTECTED_BIT
+};
+
 #ifdef _WIN32
 void vk_init_win(HINSTANCE hinst, HWND win);
 #else
@@ -104,7 +130,7 @@ 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_vattr(int pp, int loc, int bind, int offs, int stride, int fmt);
 void vk_pipeln_prim(int pp, int prim);
 void vk_pipeln_polymode(int pp, int mode);
 void vk_pipeln_cull(int pp, int cull);
@@ -123,6 +149,15 @@ void vk_pipeln_blend(int pp, int enable);
 void vk_pipeln_blendfunc(int pp, int src, int dst);
 VkPipeline vk_pipeln(int pp);
 
+int vk_create_buf(unsigned int type);
+void vk_free_buf(int bb);
+void vk_buf_usage(int bb, unsigned int type);
+void vk_buf_size(int bb, unsigned int size);
+VkBuffer vk_buf(int bb);
+void *vk_buf_map(int bb, int offs, int sz);
+void vk_buf_unmap(int bb);
+void vk_buf_data(int bb, int offs, int sz, void *data);
+
 VkShaderModule vk_load_shader(const char *fname);
 void vk_free_shader(VkShaderModule sdr);
 
@@ -133,6 +168,10 @@ VkFence vk_create_fence(int state);
 void vk_free_fence(VkFence fence);
 void vk_wait_fence(VkFence fence, long timeout);       /* -1 = inf */
 
+/* command helpers */
+void vkcmd_bind_gfxpipeln(VkCommandBuffer cmdbuf, int pp);
+void vkcmd_bind_vbuf(VkCommandBuffer cmdbuf, int count, ...);
+
 /* random helpers */
 void vk_rect(VkRect2D *r, int x, int y, int w, int h);