summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c836
1 files changed, 836 insertions, 0 deletions
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..720850a
--- /dev/null
+++ b/main.c
@@ -0,0 +1,836 @@
+#include <stdio.h>
+#include <vulkan/vulkan.h>
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_vulkan.h>
+#include <stdbool.h>
+#include <string.h>
+
+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;
+}