#include #include #include #include #include #include #include "eprintf.h" #include "nelem.h" #include "shaders.h" #include "strlist.h" #include "validation.h" #define LIST_REALLOC(l) (l)->list = erealloc((l)->list, (l)->count * sizeof *(l)->list) #define LIST_FOREACH(l, i) for (uint32_t i = 0; i < (l)->count; i++) enum { WINWIDTH = 800, WINHEIGHT = 600, }; static const char *NAME = "Vulkan Test"; enum qfams { QF_GRAPHICS, QF_PRESENT, QF_MAX, }; const char *devexts[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, }; struct swp { VkSwapchainKHR swp; struct swpimgs { VkImage *list; uint32_t count; } imgs; VkFormat format; VkExtent2D extent; }; struct ppln { VkPipeline ppln; VkPipelineLayout layout; }; struct views { VkImageView *list; uint32_t count; }; struct fbs { VkFramebuffer *list; uint32_t count; }; struct cmdbufs { VkCommandBuffer *list; uint32_t count; }; struct swpdtl { VkSurfaceCapabilitiesKHR caps; struct swpfmts { VkSurfaceFormatKHR *list; uint32_t count; } fmts; struct swppmodes { VkPresentModeKHR *list; uint32_t count; } pmodes; }; static GLFWwindow *createwin(int width, int height, const char *title) { assert(title != NULL); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); return glfwCreateWindow(width, height, title, NULL, NULL); } static void glfwexts(struct strlist *exts) { const char **gexts; uint32_t ngexts; assert(exts != NULL); gexts = glfwGetRequiredInstanceExtensions(&ngexts); sl_append(exts, gexts, ngexts); } static VkInstance createinst(void) { struct strlist layers = { 0 }, exts = { 0 }; VkInstanceCreateInfo info; VkInstance inst; VkResult res; glfwexts(&exts); validation_info(&layers, &exts); info = (VkInstanceCreateInfo){ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &(VkApplicationInfo){ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pApplicationName = NAME, .applicationVersion = VK_MAKE_VERSION(1, 0, 0), .pEngineName = NAME, .engineVersion = VK_MAKE_VERSION(1, 0, 0), .apiVersion = VK_API_VERSION_1_0, }, .enabledExtensionCount = exts.count, .ppEnabledExtensionNames = exts.list, .enabledLayerCount = layers.count, .ppEnabledLayerNames = layers.list, }; res = vkCreateInstance(&info, NULL, &inst); if (res != VK_SUCCESS) eprintf("Could not create instance"); sl_free(&layers); sl_free(&exts); return inst; } static bool hasdevexts(VkPhysicalDevice phy) { bool hasext[NELEM(devexts)] = { 0 }; VkExtensionProperties *extprops; uint32_t nextprops; vkEnumerateDeviceExtensionProperties(phy, NULL, &nextprops, NULL); extprops = emalloc(nextprops * sizeof *extprops); vkEnumerateDeviceExtensionProperties(phy, NULL, &nextprops, extprops); for (uint32_t pi = 0; pi < nextprops; pi++) { for (size_t xi = 0; xi < NELEM(devexts); xi++) { if (strcmp(devexts[xi], extprops[pi].extensionName) == 0) { hasext[xi] = true; break; } } } free(extprops); for (size_t i = 0; i < NELEM(hasext); i++) if (!hasext[i]) return false; return true; } static bool isdevsuitable(VkPhysicalDevice phy, const int *qf, const struct swpdtl *sdtl) { assert(qf != NULL); assert(sdtl != NULL); return qf[QF_GRAPHICS] >= 0 && qf[QF_PRESENT] >= 0 && hasdevexts(phy) && sdtl->fmts.count > 0 && sdtl->pmodes.count > 0; } static void getdevqfams(int *qf, VkPhysicalDevice phy, VkSurfaceKHR surf) { VkQueueFamilyProperties *qfprops; uint32_t nqfprops; assert(qf != NULL); vkGetPhysicalDeviceQueueFamilyProperties(phy, &nqfprops, NULL); qfprops = emalloc(nqfprops * sizeof *qfprops); vkGetPhysicalDeviceQueueFamilyProperties(phy, &nqfprops, qfprops); for (int i = 0; i < QF_MAX; i++) qf[i] = -1; for (uint32_t i = 0; i < nqfprops; i++) { VkQueueFamilyProperties *qfp = &qfprops[i]; VkBool32 present = false; if (qfp->queueCount <= 0) continue; if (qf[QF_GRAPHICS] < 0 && qfp->queueFlags & VK_QUEUE_GRAPHICS_BIT) qf[QF_GRAPHICS] = i; vkGetPhysicalDeviceSurfaceSupportKHR(phy, i, surf, &present); if (qf[QF_PRESENT] < 0 && present) qf[QF_PRESENT] = i; } free(qfprops); } static void getswpdtl(struct swpdtl *sdtl, VkPhysicalDevice phy, VkSurfaceKHR surf) { assert(sdtl != NULL); vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phy, surf, &sdtl->caps); vkGetPhysicalDeviceSurfaceFormatsKHR(phy, surf, &sdtl->fmts.count, NULL); LIST_REALLOC(&sdtl->fmts); vkGetPhysicalDeviceSurfaceFormatsKHR(phy, surf, &sdtl->fmts.count, sdtl->fmts.list); vkGetPhysicalDeviceSurfacePresentModesKHR(phy, surf, &sdtl->pmodes.count, NULL); LIST_REALLOC(&sdtl->pmodes); vkGetPhysicalDeviceSurfacePresentModesKHR(phy, surf, &sdtl->pmodes.count, sdtl->pmodes.list); } static void freeswpdtl(struct swpdtl *sdtl) { assert(sdtl != NULL); free(sdtl->fmts.list); free(sdtl->pmodes.list); } static VkPhysicalDevice pickphy(int *qf, struct swpdtl *sdtl, VkInstance inst, VkSurfaceKHR surf) { VkPhysicalDevice *phys, phy; uint32_t nphys; assert(qf != NULL); assert(sdtl != NULL); vkEnumeratePhysicalDevices(inst, &nphys, NULL); phys = emalloc(nphys * sizeof *phys); vkEnumeratePhysicalDevices(inst, &nphys, phys); phy = VK_NULL_HANDLE; for (uint32_t i = 0; i < nphys; i++) { getdevqfams(qf, phys[i], surf); getswpdtl(sdtl, phys[i], surf); if (isdevsuitable(phys[i], qf, sdtl)) { phy = phys[i]; break; } } if (phy == VK_NULL_HANDLE) eprintf("No suitable GPU found"); free(phys); return phy; } static int loaddqcinfs(VkDeviceQueueCreateInfo *dqcinfs, const int *qf, const float *prio) { int ndqcinfs = 0; assert(dqcinfs != NULL); assert(qf != NULL); assert(prio != NULL); for (int fi = 0; fi < QF_MAX; fi++) { for (int cii = 0; cii < fi; cii++) if (dqcinfs[cii].queueFamilyIndex == (uint32_t)qf[fi]) goto next; dqcinfs[ndqcinfs++] = (VkDeviceQueueCreateInfo){ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = qf[fi], .queueCount = 1, .pQueuePriorities = prio, }; next: ; } return ndqcinfs; } static VkDevice createdev(VkPhysicalDevice phy, const int *qf) { VkDeviceQueueCreateInfo dqcinfs[QF_MAX]; struct strlist layers = { 0 }; VkDeviceCreateInfo info; uint32_t ndqcinfs; VkDevice dev; VkResult res; assert(qf != NULL); ndqcinfs = loaddqcinfs(dqcinfs, qf, &(float){ 1 }); validation_info(&layers, NULL); info = (VkDeviceCreateInfo){ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pQueueCreateInfos = dqcinfs, .queueCreateInfoCount = ndqcinfs, .pEnabledFeatures = &(VkPhysicalDeviceFeatures){ 0 }, .enabledLayerCount = layers.count, .ppEnabledLayerNames = layers.list, .enabledExtensionCount = 1, .ppEnabledExtensionNames = devexts, }; res = vkCreateDevice(phy, &info, NULL, &dev); if (res != VK_SUCCESS) eprintf("Could not create device"); sl_free(&layers); return dev; } static VkSurfaceFormatKHR getswpfmt(struct swpfmts *scf) { assert(scf != NULL); if (scf->count == 1 && scf->list[0].format == VK_FORMAT_UNDEFINED) return (VkSurfaceFormatKHR){ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; for (uint32_t i = 0; i < scf->count; i++) if (scf->list[i].format == VK_FORMAT_B8G8R8A8_UNORM && scf->list[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) return scf->list[i]; return scf->list[0]; } static VkPresentModeKHR getswppmode(struct swppmodes *scp) { VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR; assert(scp != NULL); for (uint32_t i = 0; i < scp->count; i++) { if (scp->list[i] == VK_PRESENT_MODE_MAILBOX_KHR) return scp->list[i]; if (scp->list[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) mode = scp->list[i]; } return mode; } static VkExtent2D getswpextent(const VkSurfaceCapabilitiesKHR *caps) { VkExtent2D extent = { WINWIDTH, WINHEIGHT }; assert(caps != NULL); if (caps->currentExtent.width != UINT32_MAX) return caps->currentExtent; if (extent.width > caps->maxImageExtent.width) extent.width = caps->maxImageExtent.width; if (extent.width < caps->minImageExtent.width) extent.width = caps->minImageExtent.width; if (extent.height > caps->maxImageExtent.height) extent.height = caps->maxImageExtent.height; if (extent.height < caps->minImageExtent.height) extent.height = caps->minImageExtent.height; return extent; } static void createswp(struct swp *swp, VkSurfaceKHR surf, VkDevice dev, const int *qf, struct swpdtl *sdtl) { VkSwapchainCreateInfoKHR info; VkPresentModeKHR pmode; VkSurfaceFormatKHR fmt; uint32_t qfi[QF_MAX]; VkResult res; assert(swp != NULL); assert(qf != NULL); assert(sdtl != NULL); fmt = getswpfmt(&sdtl->fmts); swp->format = fmt.format; pmode = getswppmode(&sdtl->pmodes); swp->extent = getswpextent(&sdtl->caps); swp->imgs.count = sdtl->caps.minImageCount + 1; if (sdtl->caps.maxImageCount != 0 && swp->imgs.count > sdtl->caps.maxImageCount) swp->imgs.count = sdtl->caps.maxImageCount; for (int i = 0; i < QF_MAX; i++) qfi[i] = qf[i]; info = (VkSwapchainCreateInfoKHR){ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .surface = surf, .minImageCount = swp->imgs.count, .imageFormat = swp->format, .imageColorSpace = fmt.colorSpace, .imageExtent = swp->extent, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .imageSharingMode = qf[QF_GRAPHICS] == qf[QF_PRESENT] ? VK_SHARING_MODE_EXCLUSIVE : VK_SHARING_MODE_CONCURRENT, .queueFamilyIndexCount = QF_MAX, .pQueueFamilyIndices = qfi, .preTransform = sdtl->caps.currentTransform, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .presentMode = pmode, .clipped = VK_TRUE, .oldSwapchain = VK_NULL_HANDLE, }; res = vkCreateSwapchainKHR(dev, &info, NULL, &swp->swp); if (res != VK_SUCCESS) eprintf("Could not create swapchain"); swp->imgs = (struct swpimgs){ 0 }; vkGetSwapchainImagesKHR(dev, swp->swp, &swp->imgs.count, NULL); LIST_REALLOC(&swp->imgs); vkGetSwapchainImagesKHR(dev, swp->swp, &swp->imgs.count, swp->imgs.list); } void destroyswp(struct swp *swp, VkDevice dev) { vkDestroySwapchainKHR(dev, swp->swp, NULL); free(swp->imgs.list); } static void createviews(struct views *views, VkDevice dev, const struct swpimgs *imgs, VkFormat fmt) { VkImageViewCreateInfo info; VkResult res; assert(views != NULL); assert(imgs != NULL); *views = (struct views){ .count = imgs->count }; LIST_REALLOC(views); LIST_FOREACH(views, i) { info = (VkImageViewCreateInfo){ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = imgs->list[i], .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = fmt, .components = { .r = VK_COMPONENT_SWIZZLE_IDENTITY, .g = VK_COMPONENT_SWIZZLE_IDENTITY, .b = VK_COMPONENT_SWIZZLE_IDENTITY, .a = VK_COMPONENT_SWIZZLE_IDENTITY, }, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }, }; res = vkCreateImageView(dev, &info, NULL, &views->list[i]); if (res != VK_SUCCESS) eprintf("Could not create image view"); } } static void destroyviews(struct views *views, VkDevice dev) { assert(views != NULL); LIST_FOREACH(views, i) vkDestroyImageView(dev, views->list[i], NULL); free(views->list); } static VkShaderModule createsmod(VkDevice dev, const void *data, size_t size) { VkShaderModuleCreateInfo smcinf; VkShaderModule smod; VkResult res; assert(data != NULL); smcinf = (VkShaderModuleCreateInfo){ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = size, .pCode = data, }; res = vkCreateShaderModule(dev, &smcinf, NULL, &smod); if (res != VK_SUCCESS) eprintf("Could not create shader module"); return smod; } static VkRenderPass createpass(VkDevice dev, VkFormat fmt) { VkRenderPassCreateInfo info; VkRenderPass pass; VkResult res; info = (VkRenderPassCreateInfo){ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .attachmentCount = 1, .pAttachments = &(VkAttachmentDescription){ .format = fmt, .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, }, .subpassCount = 1, .pSubpasses = &(VkSubpassDescription){ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .colorAttachmentCount = 1, .pColorAttachments = &(VkAttachmentReference){ .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }, }, .dependencyCount = 1, .pDependencies = &(VkSubpassDependency){ .srcSubpass = VK_SUBPASS_EXTERNAL, .dstSubpass = 0, .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, .srcAccessMask = 0, .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, }, }; res = vkCreateRenderPass(dev, &info, NULL, &pass); if (res != VK_SUCCESS) eprintf("Could not create render pass"); return pass; } static void createpipeline(struct ppln *ppln, VkDevice dev, VkRenderPass pass, const VkExtent2D *extent) { VkGraphicsPipelineCreateInfo gpcinf; VkPipelineLayoutCreateInfo plcinf; VkShaderModule fsmod, vsmod; VkResult res; assert(ppln != NULL); assert(extent != NULL); plcinf = (VkPipelineLayoutCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, /* .setLayoutCount = 0, */ /* .pSetLayouts = NULL, */ /* .pushConstantRangeCount = 0, */ /* .pPushConstantRanges = 0, */ }; res = vkCreatePipelineLayout(dev, &plcinf, NULL, &ppln->layout); if (res != VK_SUCCESS) eprintf("Could not create pipeline layout"); vsmod = createsmod(dev, vertex_spv_data, vertex_spv_size); fsmod = createsmod(dev, fragment_spv_data, fragment_spv_size); gpcinf = (VkGraphicsPipelineCreateInfo){ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .stageCount = 2, .pStages = (VkPipelineShaderStageCreateInfo []){ { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = vsmod, .pName = "main", }, { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = fsmod, .pName = "main", }, }, .pVertexInputState = &(VkPipelineVertexInputStateCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .vertexBindingDescriptionCount = 0, .pVertexBindingDescriptions = NULL, .vertexAttributeDescriptionCount = 0, .pVertexAttributeDescriptions = NULL, }, .pInputAssemblyState = &(VkPipelineInputAssemblyStateCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .primitiveRestartEnable = VK_FALSE, }, .pViewportState = &(VkPipelineViewportStateCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .viewportCount = 1, .pViewports = &(VkViewport){ .x = 0.0f, .y = 0.0f, .width = (float)extent->width, .height = (float)extent->height, .minDepth = 0.0f, .maxDepth = 1.0f, }, .scissorCount = 1, .pScissors = &(VkRect2D){ .offset = { 0, 0 }, .extent = *extent, }, }, .pRasterizationState = &(VkPipelineRasterizationStateCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .depthClampEnable = VK_FALSE, .rasterizerDiscardEnable = VK_FALSE, .polygonMode = VK_POLYGON_MODE_FILL, .lineWidth = 1.0f, .cullMode = VK_CULL_MODE_BACK_BIT, .frontFace = VK_FRONT_FACE_CLOCKWISE, .depthBiasEnable = VK_FALSE, /* .depthBiasConstantFactor = 0, */ /* .depthBiasClamp = 0, */ /* .depthBiasSlopeFactor = 0, */ }, .pMultisampleState = &(VkPipelineMultisampleStateCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .sampleShadingEnable = VK_FALSE, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, /* .minSampleShading = 1, */ /* .pSampleMask = NULL, */ /* .alphaToCoverageEnable = VK_FALSE, */ /* .alphaToOneEnable = VK_FALSE, */ }, .pDepthStencilState = NULL, .pColorBlendState = &(VkPipelineColorBlendStateCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .logicOpEnable = VK_FALSE, /* .logicOp = VK_LOGIC_OP_COPY, */ .attachmentCount = 1, .pAttachments = &(VkPipelineColorBlendAttachmentState){ .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, .blendEnable = VK_FALSE, /* .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, */ /* .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, */ /* .colorBlendOp = VK_BLEND_OP_ADD, */ /* .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, */ /* .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, */ /* .alphaBlendOp = VK_BLEND_OP_ADD, */ }, /* .blendConstants[0] = 0, */ /* .blendConstants[1] = 0, */ /* .blendConstants[2] = 0, */ /* .blendConstants[3] = 0, */ }, .pDynamicState = NULL, .layout = ppln->layout, .renderPass = pass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, }; res = vkCreateGraphicsPipelines(dev, VK_NULL_HANDLE, 1, &gpcinf, NULL, &ppln->ppln); if (res != VK_SUCCESS) eprintf("Could not create graphics pipeline"); vkDestroyShaderModule(dev, fsmod, NULL); vkDestroyShaderModule(dev, vsmod, NULL); } static void destroyppln(struct ppln *ppln, VkDevice dev) { assert(ppln != NULL); vkDestroyPipeline(dev, ppln->ppln, NULL); vkDestroyPipelineLayout(dev, ppln->layout, NULL); } static void createfbs(struct fbs *fbs, VkDevice dev, const struct views *views, VkRenderPass pass, const VkExtent2D *extent) { VkFramebufferCreateInfo info; VkResult res; assert(fbs != NULL); assert(views != NULL); assert(extent != NULL); *fbs = (struct fbs){ .count = views->count }; LIST_REALLOC(fbs); LIST_FOREACH(fbs, i) { info = (VkFramebufferCreateInfo){ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .renderPass = pass, .attachmentCount = 1, .pAttachments = &views->list[i], .width = extent->width, .height = extent->height, .layers = 1, }; res = vkCreateFramebuffer(dev, &info, NULL, &fbs->list[i]); if (res != VK_SUCCESS) eprintf("Could not create framebuffer"); } } static void destroyfbs(struct fbs *fbs, VkDevice dev) { assert(fbs != NULL); LIST_FOREACH(fbs, i) vkDestroyFramebuffer(dev, fbs->list[i], NULL); free(fbs->list); } static VkCommandPool createcpool(struct cmdbufs *cmdbufs, VkDevice dev, const int *qf, const struct fbs *fbs, VkRenderPass pass, const VkExtent2D *extent, const struct ppln *ppln) { VkCommandBufferAllocateInfo cbainf; VkCommandPoolCreateInfo cpcinf; VkCommandPool cpool; VkResult res; assert(cmdbufs != NULL); assert(qf != NULL); assert(fbs != NULL); assert(extent != NULL); assert(ppln != NULL); cpcinf = (VkCommandPoolCreateInfo){ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .queueFamilyIndex = qf[QF_GRAPHICS], }; res = vkCreateCommandPool(dev, &cpcinf, NULL, &cpool); if (res != VK_SUCCESS) eprintf("Could not create command pool"); *cmdbufs = (struct cmdbufs){ .count = fbs->count }; LIST_REALLOC(cmdbufs); cbainf = (VkCommandBufferAllocateInfo){ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = cpool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = fbs->count, }; res = vkAllocateCommandBuffers(dev, &cbainf, cmdbufs->list); if (res != VK_SUCCESS) eprintf("Could not create command buffers"); LIST_FOREACH(cmdbufs, i) { vkBeginCommandBuffer(cmdbufs->list[i], &(VkCommandBufferBeginInfo){ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, /* .pInheritanceInfo = NULL, */ }); vkCmdBeginRenderPass(cmdbufs->list[i], &(VkRenderPassBeginInfo){ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderPass = pass, .framebuffer = fbs->list[i], .renderArea.offset = {0, 0}, .renderArea.extent = *extent, .clearValueCount = 1, .pClearValues = &(VkClearValue){ .color = { .float32 = { 0, 0, 0, 1 } } }, }, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(cmdbufs->list[i], VK_PIPELINE_BIND_POINT_GRAPHICS, ppln->ppln); vkCmdDraw(cmdbufs->list[i], 3, 1, 0, 0); vkCmdEndRenderPass(cmdbufs->list[i]); res = vkEndCommandBuffer(cmdbufs->list[i]); if (res != VK_SUCCESS) eprintf("Could not record command buffer"); }; return cpool; } static VkSemaphore createsem(VkDevice dev) { VkSemaphore sem; VkResult res; res = vkCreateSemaphore(dev, &(VkSemaphoreCreateInfo){ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, }, NULL, &sem); if (res != VK_SUCCESS) eprintf("Could not create semaphore"); return sem; } void drawframe(VkDevice dev, VkSwapchainKHR swp, VkQueue graphq, VkQueue presq, VkSemaphore semavail, VkSemaphore semfin, const struct cmdbufs *cmdbufs) { VkPresentInfoKHR pinf; VkSubmitInfo sinf; uint32_t i; assert(cmdbufs != NULL); vkAcquireNextImageKHR(dev, swp, UINT64_MAX, semavail, VK_NULL_HANDLE, &i); sinf = (VkSubmitInfo){ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 1, .pWaitSemaphores = &semavail, .pWaitDstStageMask = &(VkPipelineStageFlags){ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }, .commandBufferCount = 1, .pCommandBuffers = &cmdbufs->list[i], .signalSemaphoreCount = 1, .pSignalSemaphores = &semfin, }; vkQueueSubmit(graphq, 1, &sinf, VK_NULL_HANDLE); pinf = (VkPresentInfoKHR){ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .waitSemaphoreCount = 1, .pWaitSemaphores = &semfin, .swapchainCount = 1, .pSwapchains = &swp, .pImageIndices = &i, /* .pResults = NULL, */ }; vkQueuePresentKHR(presq, &pinf); } int main(void) { struct cmdbufs cmdbufs = { 0 }; VkSemaphore semavail, semfin; struct swpdtl sdtl = { 0 }; struct ppln ppln = { 0 }; struct fbs fbs = { 0 }; VkQueue graphq, presq; VkPhysicalDevice phy; VkCommandPool cpool; struct views views; VkRenderPass pass; VkSurfaceKHR surf; GLFWwindow *win; VkInstance inst; int qf[QF_MAX]; struct swp swp; VkDevice dev; VkResult res; void *cb; setprogname("vulkan"); glfwInit(); win = createwin(WINWIDTH, WINHEIGHT, NAME); inst = createinst(); cb = validation_createcb(inst); res = glfwCreateWindowSurface(inst, win, NULL, &surf); if (res != VK_SUCCESS) eprintf("Could not create window surface"); phy = pickphy(qf, &sdtl, inst, surf); dev = createdev(phy, qf); vkGetDeviceQueue(dev, qf[QF_GRAPHICS], 0, &graphq); vkGetDeviceQueue(dev, qf[QF_PRESENT], 0, &presq); createswp(&swp, surf, dev, qf, &sdtl); createviews(&views, dev, &swp.imgs, swp.format); pass = createpass(dev, swp.format); createpipeline(&ppln, dev, pass, &swp.extent); createfbs(&fbs, dev, &views, pass, &swp.extent); cpool = createcpool(&cmdbufs, dev, qf, &fbs, pass, &swp.extent, &ppln); semavail = createsem(dev); semfin = createsem(dev); while (!glfwWindowShouldClose(win)) { glfwPollEvents(); drawframe(dev, swp.swp, graphq, presq, semavail, semfin, &cmdbufs); } vkDeviceWaitIdle(dev); vkDestroySemaphore(dev, semfin, NULL); vkDestroySemaphore(dev, semavail, NULL); vkDestroyCommandPool(dev, cpool, NULL); free(cmdbufs.list); destroyfbs(&fbs, dev); destroyppln(&ppln, dev); vkDestroyRenderPass(dev, pass, NULL); destroyviews(&views, dev); destroyswp(&swp, dev); vkDestroySurfaceKHR(inst, surf, NULL); vkDestroyDevice(dev, NULL); freeswpdtl(&sdtl); validation_destroycb(inst, cb); vkDestroyInstance(inst, NULL); glfwDestroyWindow(win); glfwTerminate(); }