#include #include #include #include #include #include static SDL_Window *window = NULL; static VkInstance instance = VK_NULL_HANDLE; static VkPhysicalDevice phy_gpu = VK_NULL_HANDLE; static VkDevice gpu = VK_NULL_HANDLE; static VkSurfaceKHR surface = VK_NULL_HANDLE; static VkQueue gfx_queue; static VkQueue present_queue; static VkSwapchainKHR swapchain; static VkFormat swapchain_format; static VkExtent2D swapchain_extent; static VkRenderPass render_pass; static VkPipelineLayout pipeline_layout; static VkCommandPool command_pool; static VkCommandBuffer command_buffer; static VkPipeline graphics_pipeline; static VkSemaphore image_avail; static VkSemaphore render_finished; static VkFence in_flight_fence; static struct { size_t len; VkImage data[]; } *swapchain_images; static struct { size_t len; VkImageView data[]; } *swapchain_image_views; static struct { size_t len; VkFramebuffer data[]; } *swapchain_buffers; struct code { size_t len; uint8_t data[]; }; struct swapchain_details { VkSurfaceCapabilitiesKHR caps; struct surface_formats { size_t len; VkSurfaceFormatKHR data[]; } *formats; struct present_modes { size_t len; VkPresentModeKHR data[]; } *present_modes; }; void free_swapchain_details(struct swapchain_details details) { free(details.formats); free(details.present_modes); } struct swapchain_details query_swapchain(VkPhysicalDevice dev) { struct swapchain_details details = {0}; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, surface, &details.caps); uint32_t format_count = 0; vkGetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &format_count, NULL); if (format_count != 0) { details.formats = malloc(sizeof(*details.formats) + sizeof(*details.formats->data) * format_count); vkGetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &format_count, details.formats->data); details.formats->len = format_count; } uint32_t present_mode_count = 0; vkGetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &present_mode_count, NULL); if (present_mode_count != 0) { details.present_modes = malloc(sizeof(*details.present_modes) + sizeof(*details.present_modes->data) * format_count); vkGetPhysicalDeviceSurfacePresentModesKHR(dev, surface, &present_mode_count, details.present_modes->data); details.present_modes->len = present_mode_count; } return details; } VkSurfaceFormatKHR pick_swapchain_format(const struct surface_formats *formats) { for (size_t i = 0; i < formats->len; i++) if (formats->data[i].format == VK_FORMAT_B8G8R8A8_SRGB && formats->data[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) return formats->data[i]; return formats->data[0]; } VkPresentModeKHR pick_present_mode(const struct present_modes *modes) { for (size_t i = 0; i < modes->len; i++) if (modes->data[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR) return modes->data[i]; return VK_PRESENT_MODE_FIFO_KHR; } VkExtent2D pick_swap_extent(const VkSurfaceCapabilitiesKHR *caps) { if (caps->currentExtent.width != UINT32_MAX) return caps->currentExtent; int32_t w, h; SDL_GetWindowSize(window, &w, &h); VkExtent2D actual_extent = { .width = w > caps->minImageExtent.width ? w < caps->maxImageExtent.width ? w : caps->maxImageExtent.width : caps->minImageExtent.width, .height = h > caps->minImageExtent.height ? h < caps->maxImageExtent.height ? h : caps->maxImageExtent.height : caps->minImageExtent.height, }; return actual_extent; } struct queue_indices { enum { GRAPHICS = (1 << 0), PRESENT = (1 << 1) } found_queues; uint32_t graphics; uint32_t present; }; bool found_queue_indx(struct queue_indices inds) { return inds.found_queues == (GRAPHICS | PRESENT); } void cleanup() { vkDeviceWaitIdle(gpu); vkDestroySemaphore(gpu, image_avail, NULL); vkDestroySemaphore(gpu, render_finished, NULL); vkDestroyFence(gpu, in_flight_fence, NULL); vkDestroyCommandPool(gpu, command_pool, NULL); for (size_t i = 0; i < swapchain_buffers->len; i++) { vkDestroyFramebuffer(gpu, swapchain_buffers->data[i], NULL); } vkDestroyPipeline(gpu, graphics_pipeline, NULL); vkDestroyPipelineLayout(gpu, pipeline_layout, NULL); vkDestroyRenderPass(gpu, render_pass, NULL); for (size_t i = 0; i < swapchain_image_views->len; i++) { vkDestroyImageView(gpu, swapchain_image_views->data[i], NULL); } vkDestroySwapchainKHR(gpu, swapchain, NULL); vkDestroySurfaceKHR(instance, surface, NULL); vkDestroyDevice(gpu, NULL); vkDestroyInstance(instance, NULL); SDL_DestroyWindow(window); } void create_instance() { VkApplicationInfo app_info = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pApplicationName = "vk", .applicationVersion = VK_MAKE_VERSION(1, 0, 0), .pEngineName = "void", .engineVersion = VK_MAKE_VERSION(1, 0, 0), .apiVersion = VK_API_VERSION_1_0 }; uint32_t sdl_exts = 0; SDL_Vulkan_GetInstanceExtensions(window, &sdl_exts, NULL); const char *sdl_exts_names[sdl_exts]; SDL_Vulkan_GetInstanceExtensions(window, &sdl_exts, sdl_exts_names); for (size_t i = 0; i < sdl_exts; i++) { puts(sdl_exts_names[i]); }; VkInstanceCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &app_info, .enabledLayerCount = 0, .enabledExtensionCount = sdl_exts, .ppEnabledExtensionNames = sdl_exts_names, }; VkResult res = vkCreateInstance(&create_info, NULL, &instance); if (res != VK_SUCCESS) { fputs("failed to create vk instance", stderr); exit(-1); } } struct queue_indices find_queue_families(VkPhysicalDevice dev) { struct queue_indices queues_ind = {0}; uint32_t queue_count = 0; vkGetPhysicalDeviceQueueFamilyProperties(dev, &queue_count, NULL); VkQueueFamilyProperties *queues = calloc(queue_count, sizeof(*queues)); vkGetPhysicalDeviceQueueFamilyProperties(dev, &queue_count, queues); for (size_t i = 0; i < queue_count; i++) { if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { queues_ind.found_queues |= GRAPHICS; queues_ind.graphics = i; } VkBool32 present_support = false; vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, surface, &present_support); if (present_support) { queues_ind.found_queues |= PRESENT; queues_ind.present = i; } } free(queues); return queues_ind; } bool is_device_ok(VkPhysicalDevice dev) { uint32_t ext_count; vkEnumerateDeviceExtensionProperties(dev, NULL, &ext_count, NULL); VkExtensionProperties *props = calloc(ext_count, sizeof(*props)); vkEnumerateDeviceExtensionProperties(dev, NULL, &ext_count, props); bool swapchain = false; for (size_t i = 0; i < ext_count; i++) { if (strcmp(props[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) { swapchain = true; break; } } free(props); bool adequate_swapchain = false; if (swapchain) { struct swapchain_details details = query_swapchain(dev); adequate_swapchain = details.present_modes != NULL && details.formats != NULL; free_swapchain_details(details); } return found_queue_indx(find_queue_families(dev)) && swapchain && adequate_swapchain; } void pick_physical_dev() { uint32_t dev_count = 0; vkEnumeratePhysicalDevices(instance, &dev_count, NULL); if (dev_count == 0) { fputs("no vulkan enabled gpus", stderr); exit(-1); } VkPhysicalDevice *devs = calloc(dev_count, sizeof(*devs)); vkEnumeratePhysicalDevices(instance, &dev_count, devs); for (size_t i = 0; i < dev_count; i++) if (is_device_ok(devs[i])) { phy_gpu = devs[i]; break; } free(devs); if (phy_gpu == VK_NULL_HANDLE) { fputs("couldn't find appropriate gpu", stderr); exit(-1); } } void create_logical_dev() { struct queue_indices inds = find_queue_families(phy_gpu); float queue_prio = 1.0f; VkDeviceQueueCreateInfo queue_create_info[] = { { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = inds.graphics, .queueCount = 1, .pQueuePriorities = &queue_prio }, { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = inds.present, .queueCount = 1, .pQueuePriorities = &queue_prio }, }; VkPhysicalDeviceFeatures dev_features = { 0 }; const char * const ext = VK_KHR_SWAPCHAIN_EXTENSION_NAME; VkDeviceCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pQueueCreateInfos = queue_create_info, .queueCreateInfoCount = 2, .pEnabledFeatures = &dev_features, .enabledExtensionCount = 1, .ppEnabledExtensionNames = &ext, }; VkResult res = vkCreateDevice(phy_gpu, &create_info, NULL, &gpu); if (res != VK_SUCCESS) { fputs("failed to create logical device", stderr); exit(-1); } vkGetDeviceQueue(gpu, inds.graphics, 0, &gfx_queue); vkGetDeviceQueue(gpu, inds.present, 0, &present_queue); } void create_swapchain() { struct swapchain_details details = query_swapchain(phy_gpu); VkSurfaceFormatKHR surface_format = pick_swapchain_format(details.formats); VkPresentModeKHR present_mode = pick_present_mode(details.present_modes); VkExtent2D extent = pick_swap_extent(&details.caps); swapchain_format = surface_format.format; swapchain_extent = extent; uint32_t image_count = details.caps.minImageCount + 1; if (details.caps.maxImageCount > 0 && image_count > details.caps.maxImageCount) image_count = details.caps.maxImageCount; VkSwapchainCreateInfoKHR create_info = { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .surface = surface, .minImageCount = image_count, .imageFormat = surface_format.format, .imageColorSpace = surface_format.colorSpace, .imageExtent = extent, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .preTransform = details.caps.currentTransform, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .presentMode = present_mode, .clipped = VK_TRUE, .oldSwapchain = VK_NULL_HANDLE, }; struct queue_indices indx = find_queue_families(phy_gpu); uint32_t fam_indx[] = { indx.graphics, indx.present }; if (indx.graphics != indx.present) { create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; create_info.queueFamilyIndexCount = 2; create_info.pQueueFamilyIndices = fam_indx; } else { create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; } VkResult res = vkCreateSwapchainKHR(gpu, &create_info, NULL, &swapchain); if (res != VK_SUCCESS) { fputs("failed to create swapchain", stderr); exit(-1); } free_swapchain_details(details); uint32_t img_count = 0; vkGetSwapchainImagesKHR(gpu, swapchain, &img_count, NULL); swapchain_images = malloc(sizeof(*swapchain_images) + sizeof(*swapchain_images->data) * img_count); swapchain_images->len = img_count; vkGetSwapchainImagesKHR(gpu, swapchain, &img_count, swapchain_images->data); } void create_image_views() { swapchain_image_views = malloc(sizeof(*swapchain_image_views) + sizeof(*swapchain_image_views->data) * swapchain_images->len); swapchain_image_views->len = swapchain_images->len; for (size_t i = 0; i < swapchain_images->len; i++) { VkImageViewCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = swapchain_images->data[i], .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = swapchain_format, .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 } }; VkResult res = vkCreateImageView(gpu, &create_info, NULL, &swapchain_image_views->data[i]); if (res != VK_SUCCESS) { fputs("failed to create image view", stderr); exit(-1); } } } struct code *readfile(const char *filename) { FILE *fp = fopen(filename, "rb"); if (!fp) { fprintf(stderr, "failed to open file %s", filename); exit(-1); } fseek(fp, 0, SEEK_END); size_t len = ftell(fp); rewind(fp); struct code *bytes = malloc(sizeof(*bytes) + sizeof(*bytes->data) * len); bytes->len = len; fread(bytes->data, sizeof(*bytes->data), len, fp); fclose(fp); return bytes; } VkShaderModule create_shader_module(const struct code *code) { VkShaderModuleCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = code->len, .pCode = (uint32_t *)code->data }; VkShaderModule module; VkResult res = vkCreateShaderModule(gpu, &create_info, NULL, &module); if (res != VK_SUCCESS) { fputs("failed to create shader module.", stderr); exit(-1); } return module; } void create_graphics_pipeline() { struct code *vert_shader_code = readfile("vert.spv"); struct code *frag_shader_code = readfile("frag.spv"); VkShaderModule vert_shader = create_shader_module(vert_shader_code); VkShaderModule frag_shader = create_shader_module(frag_shader_code); VkPipelineShaderStageCreateInfo vert_shader_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = vert_shader, .pName = "main" }; VkPipelineShaderStageCreateInfo frag_shader_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = frag_shader, .pName = "main" }; VkPipelineShaderStageCreateInfo shader_stages[] = { vert_shader_info, frag_shader_info }; VkPipelineVertexInputStateCreateInfo vertex_input_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, }; VkPipelineInputAssemblyStateCreateInfo input_assembly = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .primitiveRestartEnable = VK_FALSE }; VkViewport viewport = { .x = 0.0f, .y = 0.0f, .width = swapchain_extent.width, .height = swapchain_extent.height, .minDepth = 0.0f, .maxDepth = 1.0f }; VkRect2D scissor = { .offset = { 0, 0 }, .extent = swapchain_extent }; VkDynamicState dyn_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamic_state_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .dynamicStateCount = 2, .pDynamicStates = dyn_states }; VkPipelineViewportStateCreateInfo viewport_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .viewportCount = 1, .scissorCount = 1 }; VkPipelineRasterizationStateCreateInfo rasterizer = { .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, }; VkPipelineMultisampleStateCreateInfo multisampling = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .sampleShadingEnable = VK_FALSE, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, .minSampleShading = 1.0f, }; VkPipelineColorBlendAttachmentState color_state = { .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, .blendEnable = VK_FALSE }; VkPipelineColorBlendStateCreateInfo color_blending = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .logicOpEnable = VK_FALSE, .attachmentCount = 1, .pAttachments = &color_state }; VkPipelineLayoutCreateInfo pipeline_layout_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, }; VkResult res = vkCreatePipelineLayout(gpu, &pipeline_layout_create_info, NULL, &pipeline_layout); if (res != VK_SUCCESS) { fputs("failed to create pipeline layout", stderr); exit(-1); } VkGraphicsPipelineCreateInfo pipeline_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .stageCount = 2, .pStages = shader_stages, .pVertexInputState = &vertex_input_info, .pInputAssemblyState = &input_assembly, .pViewportState = &viewport_state, .pRasterizationState = &rasterizer, .pMultisampleState = &multisampling, .pColorBlendState = &color_blending, .pDynamicState = &dynamic_state_info, .layout = pipeline_layout, .renderPass = render_pass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1 }; res = vkCreateGraphicsPipelines(gpu, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &graphics_pipeline); if (res != VK_SUCCESS) { fputs("failed to create graphics pipeline", stderr); exit(-1); } vkDestroyShaderModule(gpu, frag_shader, NULL); vkDestroyShaderModule(gpu, vert_shader, NULL); } void create_render_pass() { VkAttachmentDescription color_attach = { .format = 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, }; VkSubpassDescription subpass = { .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .colorAttachmentCount = 1, .pColorAttachments = &color_attach_ref }; VkSubpassDependency dep = { .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_WRITE_BIT }; VkRenderPassCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .attachmentCount = 1, .pAttachments = &color_attach, .subpassCount = 1, .pSubpasses = &subpass, .dependencyCount = 1, .pDependencies = &dep }; VkResult res = vkCreateRenderPass(gpu, &create_info, NULL, &render_pass); if (res != VK_SUCCESS) { fputs("failed to create render pass", stderr); exit(-1); } } void create_framebuffers() { swapchain_buffers = malloc(sizeof(*swapchain_buffers) + sizeof(*swapchain_buffers->data) * swapchain_image_views->len); swapchain_buffers->len = swapchain_image_views->len; for (size_t i = 0; i < swapchain_image_views->len; i++) { VkImageView attachs[] = { swapchain_image_views->data[i] }; VkFramebufferCreateInfo framebuffer_info = { .sType =VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .renderPass = render_pass, .attachmentCount = 1, .pAttachments = attachs, .width = swapchain_extent.width, .height = swapchain_extent.height, .layers = 1 }; VkResult res = vkCreateFramebuffer(gpu, &framebuffer_info, NULL, &swapchain_buffers->data[i]); if (res != VK_SUCCESS) { fputs("failed to create framebuffer", stderr); exit(-1); } } } void create_command_pool() { struct queue_indices indx = find_queue_families(phy_gpu); VkCommandPoolCreateInfo pool_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, .queueFamilyIndex = indx.graphics }; VkResult res = vkCreateCommandPool(gpu, &pool_info, NULL, &command_pool); if (res != VK_SUCCESS) { fputs("failed to create command pool", stderr); exit(-1); } } void create_command_buffer() { VkCommandBufferAllocateInfo alloc_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = command_pool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1 }; VkResult res = vkAllocateCommandBuffers(gpu, &alloc_info, &command_buffer); if (res != VK_SUCCESS) { fputs("failed to allocate command buffer", stderr); exit(-1); } } void record_command_buffer(VkCommandBuffer buffer, uint32_t image_index) { VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; VkResult res = vkBeginCommandBuffer(buffer, &begin_info); if (res != VK_SUCCESS) { fputs("failed to begin command buffer", stderr); exit(-1); } VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; VkRenderPassBeginInfo render_pass_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderPass = render_pass, .framebuffer = swapchain_buffers->data[image_index], .renderArea = { .extent = swapchain_extent, .offset = {0, 0} }, .clearValueCount = 1, .pClearValues = &clear_color }; vkCmdBeginRenderPass(buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); VkViewport viewport = { .x = 0.0f, .y = 0.0f, .width = swapchain_extent.width, .height = swapchain_extent.height, .minDepth = 0.0f, .maxDepth = 1.0f, }; vkCmdSetViewport(buffer, 0, 1, &viewport); VkRect2D scissor = { .offset = {0, 0}, .extent = swapchain_extent }; vkCmdSetScissor(buffer, 0, 1, &scissor); vkCmdDraw(buffer, 3, 1, 0, 0); vkCmdEndRenderPass(buffer); res = vkEndCommandBuffer(buffer); if (res != VK_SUCCESS) { fputs("failed to record command buffer", stderr); exit(-1); } } void create_sync_objects() { 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 }; if (vkCreateSemaphore(gpu, &semaphore_info, NULL, &image_avail) != VK_SUCCESS || vkCreateSemaphore(gpu, &semaphore_info, NULL, &render_finished) != VK_SUCCESS || vkCreateFence(gpu, &fence_info, NULL, &in_flight_fence) != VK_SUCCESS) { fputs("failed to create semaphores and fence", stderr); exit(-1); } } void init() { SDL_Init(SDL_INIT_VIDEO); window = SDL_CreateWindow("vk", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 800, SDL_WINDOW_VULKAN); if (!window) { fputs("failed to create sdl window", stderr); exit(-1); } uint32_t extension_count = 0; vkEnumerateInstanceExtensionProperties(NULL, &extension_count, NULL); VkExtensionProperties *exts = calloc(extension_count, sizeof(*exts)); vkEnumerateInstanceExtensionProperties(NULL, &extension_count, exts); puts("available extensions:"); for (size_t i = 0; i < extension_count; i++) { printf("\t%s\n", exts[i].extensionName); } free(exts); create_instance(); if (SDL_Vulkan_CreateSurface(window, instance, &surface) != SDL_TRUE) { fputs("failed to create surface", stderr); exit(-1); } pick_physical_dev(); create_logical_dev(); create_swapchain(); create_image_views(); create_render_pass(); create_graphics_pipeline(); create_framebuffers(); create_command_pool(); create_command_buffer(); create_sync_objects(); } void draw() { vkWaitForFences(gpu, 1, &in_flight_fence, VK_TRUE, UINT64_MAX); vkResetFences(gpu, 1, &in_flight_fence); uint32_t image_index; vkAcquireNextImageKHR(gpu, swapchain, UINT64_MAX, image_avail, VK_NULL_HANDLE, &image_index); vkResetCommandBuffer(command_buffer, 0); record_command_buffer(command_buffer, image_index); VkSemaphore wait_semaphores[] = { image_avail }; VkSemaphore signal_semaphores[] = { render_finished }; VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; VkSubmitInfo submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 1, .pWaitSemaphores = wait_semaphores, .pWaitDstStageMask = wait_stages, .signalSemaphoreCount = 1, .pSignalSemaphores = signal_semaphores }; VkResult res = vkQueueSubmit(gfx_queue, 1, &submit_info, in_flight_fence); if (res != VK_SUCCESS) { fputs("failed to submit draw command buffer", stderr); exit(-1); } VkSwapchainKHR swapchains[] = { swapchain }; VkPresentInfoKHR present_info = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .waitSemaphoreCount = 1, .pWaitSemaphores = signal_semaphores, .swapchainCount = 1, .pSwapchains = swapchains, .pImageIndices = &image_index }; vkQueuePresentKHR(present_queue, &present_info); } int main() { init(); SDL_Event e; while (SDL_PollEvent(&e), e.type != SDL_QUIT) draw(); cleanup(); return 0; }