#include "renderer.h" #include "window.h" #include "buffer.h" #include "pipeline.h" #include #include #include static VkDebugUtilsMessengerEXT debug_messenger; static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT *callback_data, void *data) { (void) severity; (void) type; (void) data; dbglogf("%s", callback_data->pMessage); return VK_FALSE; } static bool enumerate_phygpus(struct vlkn_renderer *renderer) { vkEnumeratePhysicalDevices(renderer->instance, &renderer->phy_gpus.cap, NULL); if (renderer->phy_gpus.cap == 0) return false; renderer->phy_gpus.gpus = calloc(renderer->phy_gpus.cap, sizeof(*renderer->phy_gpus.gpus)); VkPhysicalDevice gpus[renderer->phy_gpus.cap]; vkEnumeratePhysicalDevices(renderer->instance, &renderer->phy_gpus.cap, gpus); for (uint32_t i = 0; i < renderer->phy_gpus.cap; i++) { uint32_t count; vkEnumerateDeviceExtensionProperties(gpus[i], NULL, &count, NULL); VkExtensionProperties ext_props[count]; vkEnumerateDeviceExtensionProperties(gpus[i], NULL, &count, ext_props); VkPhysicalDeviceFeatures feats; vkGetPhysicalDeviceFeatures(gpus[i], &feats); if (!feats.fragmentStoresAndAtomics) { dbglog("no atomic store"); continue; } bool swapchain = false; for (size_t i = 0; i < count; i++) if (strcmp(ext_props[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) { swapchain = true; break; } if (!swapchain) continue; vkGetPhysicalDeviceQueueFamilyProperties(gpus[i], &count, NULL); VkQueueFamilyProperties queue_props[count]; vkGetPhysicalDeviceQueueFamilyProperties(gpus[i], &count, queue_props); ssize_t gfx_queue = -1, present_queue = -1; for (size_t q = 0; q < count; q++) { if ((queue_props[q].queueFlags & VK_QUEUE_GRAPHICS_BIT)) gfx_queue = q; VkBool32 present_support = false; vkGetPhysicalDeviceSurfaceSupportKHR(gpus[i], q, renderer->surface, &present_support); if (present_support) present_queue = q; } if (gfx_queue == -1 || present_queue == -1) continue; struct surface_caps caps; vkGetPhysicalDeviceSurfaceFormatsKHR(gpus[i], renderer->surface, &caps.formats.len, NULL); if (caps.formats.len == 0) return false; vkGetPhysicalDeviceSurfacePresentModesKHR(gpus[i], renderer->surface, &caps.present_modes.len, NULL); if (caps.present_modes.len == 0) return false; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(gpus[i], renderer->surface, &caps.caps); vkGetPhysicalDeviceSurfaceFormatsKHR(gpus[i], renderer->surface, &caps.formats.len, (caps.formats.data = calloc(caps.formats.len, sizeof(*caps.formats.data)))); vkGetPhysicalDeviceSurfacePresentModesKHR(gpus[i], renderer->surface, &caps.present_modes.len, (caps.present_modes.data = calloc(caps.present_modes.len, sizeof(*caps.present_modes.data)))); renderer->phy_gpus.gpus[renderer->phy_gpus.len++] = (struct phy_gpu) { .gpu = gpus[i], .graphics_queue = gfx_queue, .present_queue = present_queue, .surface_caps = caps }; if (!renderer->phy_gpus.chosen) { VkPhysicalDeviceProperties props; vkGetPhysicalDeviceProperties(gpus[i], &props); if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) renderer->phy_gpus.chosen = &renderer->phy_gpus.gpus[renderer->phy_gpus.len - 1]; } } if (renderer->phy_gpus.len == 0) return false; if (!renderer->phy_gpus.chosen) renderer->phy_gpus.chosen = &renderer->phy_gpus.gpus[0]; return true; } static VkResult create_device(struct vlkn_renderer *ren) { float queue_prio = 1.0f; VkDeviceQueueCreateInfo queue_infos[] = { { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = ren->phy_gpus.chosen->graphics_queue, .queueCount = 1, .pQueuePriorities = &queue_prio }, { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = ren->phy_gpus.chosen->present_queue, .queueCount = 1, .pQueuePriorities = &queue_prio } }; const char * const ext = VK_KHR_SWAPCHAIN_EXTENSION_NAME; VkPhysicalDeviceFeatures feats = { .fragmentStoresAndAtomics = VK_TRUE }; VkPhysicalDeviceVulkan12Features feats12 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, .runtimeDescriptorArray = VK_TRUE }; VkDeviceCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = &feats12, .pQueueCreateInfos = queue_infos, .queueCreateInfoCount = len(queue_infos), .enabledExtensionCount = 1, .ppEnabledExtensionNames = &ext, .pEnabledFeatures = &feats }; VkResult res = vkCreateDevice(ren->phy_gpus.chosen->gpu, &create_info, NULL, &ren->gpu.device); if (res != VK_SUCCESS) return res; vkGetDeviceQueue(ren->gpu.device, ren->phy_gpus.chosen->graphics_queue, 0, &ren->gpu.gfx_queue); vkGetDeviceQueue(ren->gpu.device, ren->phy_gpus.chosen->present_queue, 0, &ren->gpu.present_queue); return VK_SUCCESS; } static VkSurfaceFormatKHR pick_swapchain_format(const struct surface_caps *caps) { assert(caps && caps->formats.data && caps->formats.len > 1); for (size_t i = 0; i < caps->formats.len; i++) { VkSurfaceFormatKHR format = caps->formats.data[i]; if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) return format; } return caps->formats.data[0]; } static VkPresentModeKHR pick_present_mode(const struct surface_caps *caps) { assert(caps && caps->present_modes.data); for (size_t i = 0; i < caps->present_modes.len; i++) { VkPresentModeKHR mode = caps->present_modes.data[i]; if (mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR) return mode; } return VK_PRESENT_MODE_FIFO_KHR; } static VkResult create_renderpass(struct vlkn_renderer *ren) { VkAttachmentDescription color_attach = { .format = ren->swapchain.format, .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 }; VkAttachmentReference color_attach_ref = { .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }; VkAttachmentDescription depth_attach = { .format = VK_FORMAT_D32_SFLOAT, .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; VkAttachmentReference depth_attach_ref = { .attachment = 1, .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; VkSubpassDescription subpasses[] = { { .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .colorAttachmentCount = 1, .pColorAttachments = &color_attach_ref, .pDepthStencilAttachment = &depth_attach_ref } }; VkSubpassDependency dep = { .srcSubpass = VK_SUBPASS_EXTERNAL, .dstSubpass = 0, .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT }; VkAttachmentDescription attachs[] = { color_attach, depth_attach }; VkRenderPassCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .attachmentCount = len(attachs), .pAttachments = attachs, .subpassCount = len(subpasses), .pSubpasses = subpasses, .dependencyCount = 1, .pDependencies = &dep }; return vkCreateRenderPass(ren->gpu.device, &create_info, NULL, &ren->render_pass); } void swapchain_destroy(struct vlkn_renderer *ren); static VkResult create_swapchain(struct vlkn_renderer *ren) { struct surface_caps *caps = &ren->phy_gpus.chosen->surface_caps; VkSurfaceFormatKHR format = pick_swapchain_format(caps); ren->swapchain.format = format.format; ren->swapchain.extent = caps->caps.currentExtent.width != UINT32_MAX ? caps->caps.currentExtent : (VkExtent2D) { .width = ren->win->width, .height = ren->win->height }; uint32_t image_count = caps->caps.minImageCount + 1; if (caps->caps.maxImageCount > 0 && image_count > caps->caps.maxImageCount) image_count = caps->caps.maxImageCount; // TODO: understand those values VkSwapchainCreateInfoKHR create_info = { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .surface = ren->surface, .minImageCount = image_count, .imageFormat = ren->swapchain.format, .imageColorSpace = format.colorSpace, .imageExtent = ren->swapchain.extent, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .preTransform = caps->caps.currentTransform, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .presentMode = pick_present_mode(caps), .clipped = VK_TRUE, }; uint32_t queue_families[] = { ren->phy_gpus.chosen->present_queue, ren->phy_gpus.chosen->graphics_queue }; if (queue_families[0] == queue_families[1]) { create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; create_info.queueFamilyIndexCount = 2; create_info.pQueueFamilyIndices = queue_families; } else { create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; } VkResult res = vkCreateSwapchainKHR(ren->gpu.device, &create_info, NULL, &ren->swapchain.swapchain); if (res != VK_SUCCESS) return false; if (ren->render_pass == VK_NULL_HANDLE) { res = create_renderpass(ren); if (res != VK_SUCCESS) return res; } vkGetSwapchainImagesKHR(ren->gpu.device, ren->swapchain.swapchain, &ren->swapchain.images.len, NULL); ren->swapchain.images.data = calloc(ren->swapchain.images.len, sizeof(*ren->swapchain.images.data)); VkImage images[ren->swapchain.images.len]; vkGetSwapchainImagesKHR(ren->gpu.device, ren->swapchain.swapchain, &ren->swapchain.images.len, images); struct image_opts opts = { .extent = ren->swapchain.extent, .mip_level = 1, .format = VK_FORMAT_D32_SFLOAT, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, .mem_props = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, .aspect = VK_IMAGE_ASPECT_DEPTH_BIT }; res = image_create(ren, opts, &ren->depth); if (res != VK_SUCCESS) return res; for (size_t i = 0; i < ren->swapchain.images.len; i++) { ren->swapchain.images.data->image = images[i]; res = image_view_create(ren, ren->swapchain.format, VK_IMAGE_ASPECT_COLOR_BIT, images[i], &ren->swapchain.images.data[i].view); if (res != VK_SUCCESS) return res; VkImageView attachs[] = { ren->swapchain.images.data[i].view, ren->depth.view }; VkFramebufferCreateInfo fb_info = { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .renderPass = ren->render_pass, .attachmentCount = len(attachs), .pAttachments = attachs, .width = ren->swapchain.extent.width, .height = ren->swapchain.extent.height, .layers = 1 }; res = vkCreateFramebuffer(ren->gpu.device, &fb_info, NULL, &ren->swapchain.images.data[i].framebuffer); if (res != VK_SUCCESS) return res; } return VK_SUCCESS; } static VkResult create_sync_objects(struct vlkn_renderer *ren) { VkSemaphoreCreateInfo semaphore_info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; VkFenceCreateInfo fence_info = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .flags = VK_FENCE_CREATE_SIGNALED_BIT }; VkResult res; #define X(exp) do { res = exp; if (res != VK_SUCCESS) return res; } while (0) for (size_t i = 0; i < MAX_FRAMES; i++) { X(vkCreateSemaphore(ren->gpu.device, &semaphore_info, NULL, &ren->locks.image_available[i])); X(vkCreateSemaphore(ren->gpu.device, &semaphore_info, NULL, &ren->locks.render_finished[i])); X(vkCreateFence(ren->gpu.device, &fence_info, NULL, &ren->locks.in_flight[i])); } #undef X return VK_SUCCESS; } static VkResult create_command_pool(struct vlkn_renderer *ren) { VkCommandPoolCreateInfo pool_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, .queueFamilyIndex = ren->phy_gpus.chosen->graphics_queue }; VkResult res = vkCreateCommandPool(ren->gpu.device, &pool_info, NULL, &ren->command.pool); if (res != VK_SUCCESS) return res; VkCommandBufferAllocateInfo alloc_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = ren->command.pool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = MAX_FRAMES }; return vkAllocateCommandBuffers(ren->gpu.device, &alloc_info, ren->command.buffers); } void resize_callback(void *data) { struct vlkn_renderer *ren = data; if (ren->win->width == 0 || ren->win->height == 0) return; vkDeviceWaitIdle(ren->gpu.device); swapchain_destroy(ren); create_swapchain(ren); } struct vlkn_renderer *vlkn_renderer_init(struct vlkn_window *win) { struct vlkn_renderer *ren = calloc(1, sizeof(*ren)); if (!ren) return NULL; ren->win = win; VkApplicationInfo app_info = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pApplicationName = win->title ? win->title : "", .applicationVersion = VK_MAKE_VERSION(1, 0, 0), .pEngineName = "void", .engineVersion = VK_MAKE_VERSION(1, 0, 0), .apiVersion = VK_API_VERSION_1_3 }; // TODO: query window const char *exts[] = { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, VK_EXT_DEBUG_UTILS_EXTENSION_NAME, }; const char *validation_layers[] = { "VK_LAYER_KHRONOS_validation" }; VkInstanceCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &app_info, .enabledExtensionCount = len(exts), .ppEnabledExtensionNames = exts, .enabledLayerCount = 1, .ppEnabledLayerNames = validation_layers }; if (!vlkn_check(vkCreateInstance(&create_info, NULL, &ren->instance))) goto err; VkDebugUtilsMessengerCreateInfoEXT debug_create_info = { .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, .messageSeverity = //VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, .pfnUserCallback = debug_callback }; ((PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(ren->instance, "vkCreateDebugUtilsMessengerEXT"))(ren->instance, &debug_create_info, NULL, &debug_messenger); ren->surface = window_create_vk_surface(ren->instance, win); if (ren->surface == VK_NULL_HANDLE) { dbglog("failed to create window surface"); goto err; } if (!enumerate_phygpus(ren)) { dbglog("failed to enumerate physical gpus"); goto err; } if (!vlkn_check(create_device(ren))) goto err; if (!vlkn_check(create_swapchain(ren))) goto err; if (!vlkn_check(create_sync_objects(ren))) goto err; if (!vlkn_check(create_command_pool(ren))) goto err; window_on_resize(win, resize_callback, ren); goto out; err: ren = (vlkn_renderer_destroy(ren), NULL); out: return ren; } void swapchain_destroy(struct vlkn_renderer *ren) { image_destroy(ren, &ren->depth); for (size_t i = 0; i < ren->swapchain.images.len; i++) { vkDestroyFramebuffer(ren->gpu.device, ren->swapchain.images.data[i].framebuffer, NULL); vkDestroyImageView(ren->gpu.device, ren->swapchain.images.data[i].view, NULL); } free(ren->swapchain.images.data); vkDestroySwapchainKHR(ren->gpu.device, ren->swapchain.swapchain, NULL); } void vlkn_renderer_destroy(struct vlkn_renderer *renderer) { if (!renderer) return; if (renderer->gpu.device) { vkDeviceWaitIdle(renderer->gpu.device); for (size_t i = 0; i < MAX_FRAMES; i++) { vkDestroySemaphore(renderer->gpu.device, renderer->locks.image_available[i], NULL); vkDestroySemaphore(renderer->gpu.device, renderer->locks.render_finished[i], NULL); vkDestroyFence(renderer->gpu.device, renderer->locks.in_flight[i], NULL); } swapchain_destroy(renderer); vkDestroyCommandPool(renderer->gpu.device, renderer->command.pool, NULL); vkDestroyPipeline(renderer->gpu.device, renderer->pipeline.gfx, NULL); vkDestroyPipeline(renderer->gpu.device, renderer->pipeline.blend, NULL); vkDestroyPipelineLayout(renderer->gpu.device, renderer->pipeline.layout, NULL); vkDestroyRenderPass(renderer->gpu.device, renderer->render_pass, NULL); vkDestroyDevice(renderer->gpu.device, NULL); } if (renderer->instance) { vkDestroySurfaceKHR(renderer->instance, renderer->surface, NULL); ((PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(renderer->instance, "vkDestroyDebugUtilsMessengerEXT"))(renderer->instance, debug_messenger, NULL); vkDestroyInstance(renderer->instance, NULL); } free(renderer); } uint32_t idx; void vlkn_render(struct vlkn_renderer *renderer) { size_t frame = 0; vkWaitForFences(renderer->gpu.device, 1, &renderer->locks.in_flight[frame], VK_TRUE, UINT64_MAX); vkResetFences(renderer->gpu.device, 1, &renderer->locks.in_flight[frame]); VkResult res = vkAcquireNextImageKHR(renderer->gpu.device, renderer->swapchain.swapchain, UINT64_MAX, renderer->locks.image_available[frame], VK_NULL_HANDLE, &idx); switch (res) { case VK_SUCCESS: case VK_SUBOPTIMAL_KHR: break; case VK_ERROR_OUT_OF_DATE_KHR: resize_callback(renderer); return; default: dbglog("failed to aquire swapchain images"); abort(); } VkCommandBuffer buffer = renderer->command.buffers[frame]; vkResetCommandBuffer(renderer->command.buffers[frame], 0); VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; if (!vlkn_check(vkBeginCommandBuffer(buffer, &begin_info))) return; VkClearValue clear_color[] = { { .color = {{ 0.0f, 0.0f, 0.0f, 1.0f }} }, { .depthStencil = { 1.0f, 0.0f }} }; VkRenderPassBeginInfo render_pass_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderPass = renderer->render_pass, .framebuffer = renderer->swapchain.images.data[idx].framebuffer, .renderArea = { .extent = renderer->swapchain.extent, .offset = {0, 0} }, .clearValueCount = len(clear_color), .pClearValues = clear_color }; vkCmdBeginRenderPass(buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); VkViewport viewport = { .x = 0.0f, .y = 0.0f, .width = renderer->swapchain.extent.width, .height = renderer->swapchain.extent.height, .minDepth = 0.0f, .maxDepth = 1.0f, }; vkCmdSetViewport(buffer, 0, 1, &viewport); VkRect2D scissor = { .offset = {0, 0}, .extent = renderer->swapchain.extent }; vkCmdSetScissor(buffer, 0, 1, &scissor); } void vlkn_draw(struct vlkn_renderer *renderer, struct vlkn_pipeline *pipeline, size_t mesh_len, struct vlkn_mesh meshes[mesh_len]) { size_t frame = 0; VkCommandBuffer buffer = renderer->command.buffers[frame]; vkCmdBindPipeline(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); /* vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->layout, 0, 1, &desc_sets[current_frame], 0, NULL); */ for (size_t i = 0; i < mesh_len; i++) { vkCmdBindVertexBuffers(buffer, 0, 1, &meshes[i].vertex.buffer, (VkDeviceSize[]){0}); vkCmdBindIndexBuffer(buffer, meshes[i].index.buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdDrawIndexed(buffer, meshes[i].index_count, 1, 0, 0, 0); vkCmdPushConstants(buffer, pipeline->layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(struct vec3), &meshes[i].position); } } void vlkn_present(struct vlkn_renderer *renderer) { size_t frame = 0; VkCommandBuffer buffer = renderer->command.buffers[frame]; vkCmdEndRenderPass(buffer); vlkn_check(vkEndCommandBuffer(buffer)); VkSubmitInfo submit = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 1, .pWaitSemaphores = &renderer->locks.image_available[frame], .pWaitDstStageMask = (VkPipelineStageFlags[]) { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }, .signalSemaphoreCount = 1, .pSignalSemaphores = &renderer->locks.render_finished[frame], .commandBufferCount = 1, .pCommandBuffers = &renderer->command.buffers[frame] }; if (!vlkn_check(vkQueueSubmit(renderer->gpu.gfx_queue, 1, &submit, renderer->locks.in_flight[frame]))) return; VkPresentInfoKHR present = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .waitSemaphoreCount = 1, .pWaitSemaphores = &renderer->locks.render_finished[frame], .swapchainCount = 1, .pSwapchains = &renderer->swapchain.swapchain, .pImageIndices = &idx }; switch (vkQueuePresentKHR(renderer->gpu.present_queue, &present)) { case VK_SUCCESS: break; case VK_SUBOPTIMAL_KHR: case VK_ERROR_OUT_OF_DATE_KHR: resize_callback(renderer); break; default: abort(); return; } wl_display_roundtrip(renderer->win->dpy); //ren->current_frame = (ren->current_frame + 1) % MAX_FRAMES; }