#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"
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 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 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;
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_next_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;
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;
if(!framebufs) {
framebufs = darr_alloc(0, sizeof *framebufs);
- darr_push(framebufs, &framebuf); /* add dummy rpass */
+ darr_push(framebufs, &framebuf); /* add dummy framebuffer */
}
for(i=1; i<darr_size(framebufs); i++) {
}
+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);
+}
+
#define ARRSZ(arr) (sizeof arr / sizeof *arr)
static const char *known_layer_list[] = {
"VK_LAYER_GOOGLE_threading",
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)
{