From 01ba05cad0f685fa953d7db490f0105d09a6b6d8 Mon Sep 17 00:00:00 2001 From: "Anna (navi) Figueiredo Gomes" Date: Wed, 27 Dec 2023 01:11:12 +0100 Subject: initial Signed-off-by: Anna (navi) Figueiredo Gomes --- Makefile | 16 ++ main.c | 836 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ shader.frag | 8 + shader.vert | 20 ++ 4 files changed, 880 insertions(+) create mode 100644 Makefile create mode 100644 main.c create mode 100644 shader.frag create mode 100644 shader.vert diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d7a272c --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +CFLAGS = -O2 -g +LDFLAGS != pkg-config --libs vulkan sdl2 + +vulkan: main.c vert.spv frag.spv + gcc $(CFLAGS) -o vk main.c $(LDFLAGS) + +%.spv: shader.% + glslc $^ -o $@ + +.PHONY: test clean + +test: vulkan + ./vk + +clean: + rm -f vk *.spv 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 +#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; +} diff --git a/shader.frag b/shader.frag new file mode 100644 index 0000000..c9e559f --- /dev/null +++ b/shader.frag @@ -0,0 +1,8 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} diff --git a/shader.vert b/shader.vert new file mode 100644 index 0000000..9e69113 --- /dev/null +++ b/shader.vert @@ -0,0 +1,20 @@ +#version 450 + +layout(location = 0) out vec3 fragColor; + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + fragColor = colors[gl_VertexIndex]; +} -- cgit v1.2.3