diff --git a/Makefile b/Makefile index 0219b10..6317453 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,11 @@ GDB = lldb SOURCES = $(wildcard src/*.c) OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) +VERT_SPV = $(addsuffix .vert.spv, $(basename $(wildcard shader_src/*.vert))) +FRAG_SPV = $(addsuffix .frag.spv, $(basename $(wildcard shader_src/*.frag))) + +.PHONY: all +all: spacegame $(VERT_SPV) $(FRAG_SPV) spacegame: $(OBJECTS) $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) @@ -34,6 +39,8 @@ compile_commands.json: $(COMPDB_ENTRIES) .PHONY: clean clean_compdb clean: + rm $(FRAG_SPV) + rm $(VERT_SPV) rm $(OBJECTS) rm spacegame @@ -46,3 +53,10 @@ run: spacegame debug: spacegame $(GDB) spacegame + + +%.vert.spv: %.vert + glslc $< -o $@ + +%.frag.spv: %.frag + glslc $< -o $@ diff --git a/shader_src/basic.frag b/shader_src/basic.frag new file mode 100644 index 0000000..7c5b0e7 --- /dev/null +++ b/shader_src/basic.frag @@ -0,0 +1,9 @@ +#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_src/basic.vert b/shader_src/basic.vert new file mode 100644 index 0000000..f5b2f8d --- /dev/null +++ b/shader_src/basic.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]; +} diff --git a/src/main.c b/src/main.c index 98e3bbd..c9b1a2c 100644 --- a/src/main.c +++ b/src/main.c @@ -35,6 +35,11 @@ typedef struct SwapchainDetailsStruct { uint32_t present_modes_count; } SwapchainDetails; +typedef struct SwapchainImagesStruct { + VkImage* images; + uint32_t count; +} SwapchainImages; + typedef struct VulkanContextStruct { VkInstance instance; VkDebugUtilsMessengerEXT debug_messenger; @@ -48,6 +53,27 @@ typedef struct VulkanContextStruct { SwapchainDetails swapchain_details; VkSwapchainKHR swapchain; + + VkSurfaceFormatKHR swapchain_format; + VkPresentModeKHR swapchain_present_mode; + VkExtent2D swapchain_extent; + + uint32_t swapchain_image_count; + VkImage* swapchain_images; + VkImageView* swapchain_image_views; + VkFramebuffer* swapchain_framebuffers; + + VkRenderPass render_pass; + + VkCommandPool graphics_command_pool; + + VkCommandBuffer triangle_command_buffer; + VkPipelineLayout triangle_pipeline_layout; + VkPipeline triangle_pipeline; + + VkSemaphore image_available_semaphore; + VkSemaphore render_finished_semaphore; + VkFence in_flight_fence; } VulkanContext; const char * validation_layers[] = { @@ -433,13 +459,9 @@ VkExtent2D choose_swapchain_extent(SwapchainDetails swapchain_details) { return swapchain_details.capabilities.currentExtent; } -VkSwapchainKHR create_swapchain(VkDevice device, SwapchainDetails swapchain_details, VkSurfaceKHR surface, QueueIndices indices, VkSwapchainKHR old_swapchain) { - VkSurfaceFormatKHR format = choose_swapchain_format(swapchain_details); - VkPresentModeKHR present_mode = choose_present_mode(swapchain_details); - VkExtent2D extent = choose_swapchain_extent(swapchain_details); - - uint32_t image_count = swapchain_details.capabilities.minImageCount + 1; - uint32_t max_images = swapchain_details.capabilities.maxImageCount; +VkSwapchainKHR create_swapchain(VkDevice device, VkSurfaceFormatKHR format, VkPresentModeKHR present_mode, VkExtent2D extent, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR capabilities, QueueIndices indices, VkSwapchainKHR old_swapchain) { + uint32_t image_count = capabilities.minImageCount + 1; + uint32_t max_images = capabilities.maxImageCount; if((max_images > 0) && (image_count > max_images)) { image_count = max_images; } @@ -465,7 +487,7 @@ VkSwapchainKHR create_swapchain(VkDevice device, SwapchainDetails swapchain_deta swapchain_info.pQueueFamilyIndices = 0; } - swapchain_info.preTransform = swapchain_details.capabilities.currentTransform; + swapchain_info.preTransform = capabilities.currentTransform; swapchain_info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; swapchain_info.presentMode = present_mode; swapchain_info.clipped = VK_TRUE; @@ -482,6 +504,430 @@ VkSwapchainKHR create_swapchain(VkDevice device, SwapchainDetails swapchain_deta return swapchain; } +SwapchainImages get_swapchain_images(VkDevice device, VkSwapchainKHR swapchain) { + SwapchainImages images; + images.images = 0; + images.count = 0; + + VkResult result; + result = vkGetSwapchainImagesKHR(device, swapchain, &images.count, 0); + if(result != VK_SUCCESS) { + images.count = 0; + return images; + } + + images.images = malloc(sizeof(VkImage)*images.count); + if(images.images == 0) { + images.count = 0; + return images; + } + + result = vkGetSwapchainImagesKHR(device, swapchain, &images.count, images.images); + if(result != VK_SUCCESS) { + images.count = 0; + return images; + } + + return images; +} + +VkImageView* create_image_views(VkDevice device, uint32_t image_count, VkImage* images, VkSurfaceFormatKHR format) { + VkImageView* image_views = malloc(sizeof(VkImageView)*image_count); + if(image_views == 0) { + return 0; + } + + for(uint32_t i = 0; i < image_count; i++) { + VkImageViewCreateInfo view_info = {}; + view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_info.image = images[i]; + view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_info.format = format.format; + + view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_info.subresourceRange.baseMipLevel = 0; + view_info.subresourceRange.levelCount = 1; + view_info.subresourceRange.baseArrayLayer = 0; + view_info.subresourceRange.layerCount = 1; + + VkResult result = vkCreateImageView(device, &view_info, 0, &image_views[i]); + if(result != VK_SUCCESS) { + free(image_views); + return 0; + } + } + + return image_views; +} + +VkFramebuffer* create_swapchain_framebuffers(VkDevice device, uint32_t image_count, VkImageView* image_views, VkRenderPass render_pass, VkExtent2D extent) { + VkFramebuffer* framebuffers = malloc(sizeof(VkFramebuffer)*image_count); + if(framebuffers == 0) { + return 0; + } + + for(uint32_t i = 0; i < image_count; i++) { + VkImageView attachments[] = { + image_views[i], + }; + + VkFramebufferCreateInfo framebuffer_info = {}; + framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_info.renderPass = render_pass; + framebuffer_info.attachmentCount = 1; + framebuffer_info.pAttachments = attachments; + framebuffer_info.width = extent.width; + framebuffer_info.height = extent.height; + framebuffer_info.layers = 1; + + VkResult result = vkCreateFramebuffer(device, &framebuffer_info, 0, &framebuffers[i]); + if(result != VK_SUCCESS) { + free(framebuffers); + return 0; + } + } + + return framebuffers; +} + +VkShaderModule create_shader_module(VkDevice device, const char * code, uint32_t code_size) { + VkShaderModuleCreateInfo shader_info = {}; + shader_info.sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT; + shader_info.codeSize = code_size; + shader_info.pCode = (uint32_t*)code; + + VkShaderModule shader; + VkResult result; + result = vkCreateShaderModule(device, &shader_info, 0, &shader); + if(result != VK_SUCCESS) { + return VK_NULL_HANDLE; + } + + return shader; +} + +VkShaderModule load_shader_file(uint32_t buffer_size, const char* path, VkDevice device) { + FILE* file; + file = fopen(path, "r"); + if(file == 0) { + return VK_NULL_HANDLE; + } + + char * buffer = malloc(buffer_size); + if(buffer == 0) { + return VK_NULL_HANDLE; + } + + size_t read = fread(buffer, 1, buffer_size, file); + + VkShaderModule shader = create_shader_module(device, buffer, read); + free(buffer); + return shader; +} + +VkRenderPass create_render_pass(VkDevice device, VkSurfaceFormatKHR format) { + VkAttachmentDescription color_attachment; + color_attachment.format = format.format; + color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; + color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference color_attachment_ref = {}; + color_attachment_ref.attachment = 0; + color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment_ref; + + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo render_info = {}; + render_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_info.attachmentCount = 1; + render_info.pAttachments = &color_attachment; + render_info.subpassCount = 1; + render_info.pSubpasses = &subpass; + render_info.dependencyCount = 1; + render_info.pDependencies = &dependency; + + VkRenderPass render_pass; + VkResult result = vkCreateRenderPass(device, &render_info, 0, &render_pass); + if(result != VK_SUCCESS) { + return VK_NULL_HANDLE; + } + + return render_pass; +} + +VkPipelineLayout create_pipeline_layout(VkDevice device, uint32_t set_count, VkDescriptorSetLayout* sets, uint32_t pcr_count, VkPushConstantRange* pcrs) { + VkPipelineLayout layout; + VkPipelineLayoutCreateInfo layout_info = {}; + layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + layout_info.setLayoutCount = set_count; + layout_info.pSetLayouts = sets; + layout_info.pushConstantRangeCount = pcr_count; + layout_info.pPushConstantRanges = pcrs; + + VkResult result; + result = vkCreatePipelineLayout(device, &layout_info, 0, &layout); + if(result != VK_SUCCESS) { + return VK_NULL_HANDLE; + } + + return layout; +} + +VkPipeline create_graphics_pipeline(VkDevice device, VkExtent2D extent, VkPipelineLayout layout, VkRenderPass render_pass) { + VkShaderModule vert_shader = load_shader_file(2048, "vert.spv", device); + fprintf(stderr, "Loaded vert_shader: %p\n", vert_shader); + VkShaderModule frag_shader = load_shader_file(2048, "frag.spv", device); + fprintf(stderr, "Loaded frag_shader: %p\n", frag_shader); + + VkPipelineShaderStageCreateInfo shader_stages[2] = {}; + shader_stages[0].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT; + shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + shader_stages[0].module = vert_shader; + shader_stages[0].pName = "main"; + + shader_stages[1].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT; + shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + shader_stages[1].module = frag_shader; + shader_stages[1].pName = "main"; + + VkDynamicState dynamic_states[] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }; + uint32_t dynamic_state_count = sizeof(dynamic_states)/sizeof(VkDynamicState); + + VkPipelineDynamicStateCreateInfo dynamic_info = {}; + dynamic_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_info.dynamicStateCount = dynamic_state_count; + dynamic_info.pDynamicStates = dynamic_states; + + VkPipelineVertexInputStateCreateInfo vertex_input_info = {}; + vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input_info.vertexBindingDescriptionCount = 0; + vertex_input_info.pVertexBindingDescriptions = 0; + vertex_input_info.vertexAttributeDescriptionCount = 0; + vertex_input_info.pVertexAttributeDescriptions = 0; + + VkPipelineInputAssemblyStateCreateInfo input_assemvly_info = {}; + input_assemvly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assemvly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assemvly_info.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)(extent.width); + viewport.height = (float)(extent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + VkOffset2D scissor_offset = {.x = 0, .y = 0}; + scissor.offset = scissor_offset; + scissor.extent = extent; + + VkPipelineViewportStateCreateInfo viewport_state = {}; + viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.viewportCount = 1; + viewport_state.pViewports = &viewport; + viewport_state.scissorCount = 1; + viewport_state.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo raster_info = {}; + raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + raster_info.depthClampEnable = VK_FALSE; + raster_info.rasterizerDiscardEnable = VK_FALSE; + raster_info.polygonMode = VK_POLYGON_MODE_FILL; + raster_info.lineWidth = 1.0f; + raster_info.cullMode = VK_CULL_MODE_BACK_BIT; + raster_info.frontFace = VK_FRONT_FACE_CLOCKWISE; + raster_info.depthBiasEnable = VK_FALSE; + raster_info.depthBiasConstantFactor = 0.0f; + raster_info.depthBiasClamp = 0.0f; + raster_info.depthBiasSlopeFactor = 0.0f; + + VkPipelineMultisampleStateCreateInfo multisample_info = {}; + multisample_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisample_info.sampleShadingEnable = VK_FALSE; + multisample_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisample_info.minSampleShading = 1.0f; + multisample_info.pSampleMask = 0; + multisample_info.alphaToCoverageEnable = VK_FALSE; + multisample_info.alphaToOneEnable = VK_FALSE; + + VkPipelineColorBlendAttachmentState color_blend_attachment = {}; + color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + color_blend_attachment.blendEnable = VK_TRUE; + color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD; + + VkPipelineColorBlendStateCreateInfo color_blend_info = {}; + color_blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blend_info.logicOpEnable = VK_FALSE; + color_blend_info.logicOp = VK_LOGIC_OP_COPY; + color_blend_info.attachmentCount = 1; + color_blend_info.pAttachments = &color_blend_attachment; + color_blend_info.blendConstants[0] = 0.0f; + color_blend_info.blendConstants[1] = 0.0f; + color_blend_info.blendConstants[2] = 0.0f; + color_blend_info.blendConstants[3] = 0.0f; + + VkGraphicsPipelineCreateInfo pipeline_info = {}; + pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info.stageCount = 2; + pipeline_info.pStages = shader_stages; + pipeline_info.pVertexInputState = &vertex_input_info; + pipeline_info.pInputAssemblyState = &input_assemvly_info; + pipeline_info.pViewportState = &viewport_state; + pipeline_info.pRasterizationState = &raster_info; + pipeline_info.pDepthStencilState = 0; + pipeline_info.pColorBlendState = &color_blend_info; + pipeline_info.pDynamicState = &dynamic_info; + pipeline_info.layout = layout; + pipeline_info.renderPass = render_pass; + pipeline_info.subpass = 0; + + pipeline_info.basePipelineHandle = VK_NULL_HANDLE; + pipeline_info.basePipelineIndex = -1; + + VkPipeline pipeline; + VkResult result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_info, 0, &pipeline); + if(result != VK_SUCCESS) { + return VK_NULL_HANDLE; + } + + return pipeline; +} + +VkCommandPool create_command_pool(VkDevice device, uint32_t queue_family) { + VkCommandPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + pool_info.queueFamilyIndex = queue_family; + + VkCommandPool command_pool; + VkResult result = vkCreateCommandPool(device, &pool_info, 0, &command_pool); + if(result != VK_SUCCESS) { + return VK_NULL_HANDLE; + } + + return command_pool; +} + +VkResult record_command_buffer_triangle(VkCommandBuffer command_buffer, uint32_t image_index, VkRenderPass render_pass, VkFramebuffer* framebuffers, VkExtent2D extent, VkPipeline graphics_pipeline) { + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = 0; + begin_info.pInheritanceInfo = 0; + + VkResult result = vkBeginCommandBuffer(command_buffer, &begin_info); + if(result != VK_SUCCESS) { + return result; + } + + VkRenderPassBeginInfo render_pass_info = {}; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_info.renderPass = render_pass; + render_pass_info.framebuffer = framebuffers[image_index]; + VkOffset2D render_offset = {.x = 0, .y = 0}; + render_pass_info.renderArea.offset = render_offset; + render_pass_info.renderArea.extent = extent; + VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + render_pass_info.clearValueCount = 1; + render_pass_info.pClearValues = &clear_color; + + vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); + + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)(extent.width); + viewport.height = (float)(extent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(command_buffer, 0, 1, &viewport); + + VkRect2D scissor = {}; + VkOffset2D scissor_offset = {.x = 0.0f, .y = 0.0f}; + scissor.offset = scissor_offset; + scissor.extent = extent; + vkCmdSetScissor(command_buffer, 0, 1, &scissor); + + vkCmdDraw(command_buffer, 3, 1, 0, 0); + vkCmdEndRenderPass(command_buffer); + + return vkEndCommandBuffer(command_buffer); +} + +VkCommandBuffer create_command_buffer(VkDevice device, VkCommandPool command_pool) { + VkCommandBufferAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = 1; + + VkCommandBuffer command_buffer; + VkResult result = vkAllocateCommandBuffers(device, &alloc_info, &command_buffer); + if(result != VK_SUCCESS) { + return VK_NULL_HANDLE; + } + + return command_buffer; +} + +VkSemaphore create_semaphore(VkDevice device, VkSemaphoreCreateFlags flags) { + VkSemaphoreCreateInfo semaphore_info = {}; + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphore_info.flags = flags; + + VkSemaphore semaphore; + VkResult result = vkCreateSemaphore(device, &semaphore_info, 0, &semaphore); + if(result != VK_SUCCESS) { + return VK_NULL_HANDLE; + } + + return semaphore; +} + +VkFence create_fence(VkDevice device, VkFenceCreateFlags flags) { + VkFenceCreateInfo fence_info = {}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_info.flags = flags; + + VkFence fence; + VkResult result = vkCreateFence(device, &fence_info, 0, &fence); + if(result != VK_SUCCESS) { + return VK_NULL_HANDLE; + } + return fence; +} + VulkanContext* init_vulkan(GLFWwindow* window) { VulkanContext* context = (VulkanContext*)malloc(sizeof(VulkanContext)); @@ -543,19 +989,170 @@ VulkanContext* init_vulkan(GLFWwindow* window) { context->swapchain_details = maybe_details.details; } - VkSwapchainKHR swapchain = create_swapchain(context->device, context->swapchain_details, context->surface, context->queue_indices, VK_NULL_HANDLE); + context->swapchain_format = choose_swapchain_format(context->swapchain_details); + context->swapchain_present_mode = choose_present_mode(context->swapchain_details); + context->swapchain_extent = choose_swapchain_extent(context->swapchain_details); + + VkSwapchainKHR swapchain = create_swapchain(context->device, context->swapchain_format, context->swapchain_present_mode, context->swapchain_extent, context->surface, context->swapchain_details.capabilities, context->queue_indices, VK_NULL_HANDLE); if(swapchain == VK_NULL_HANDLE) { + fprintf(stderr, "failed to create vulkan swapchain\n"); return 0; } else { context->swapchain = swapchain; } + SwapchainImages swapchain_images = get_swapchain_images(context->device, context->swapchain); + if(swapchain_images.count == 0) { + fprintf(stderr, "failed to get vulkan swapchain images\n"); + return 0; + } else { + context->swapchain_image_count = swapchain_images.count; + context->swapchain_images = swapchain_images.images; + } + + VkImageView* image_views = create_image_views(context->device, context->swapchain_image_count, context->swapchain_images, context->swapchain_format); + if(image_views == 0) { + fprintf(stderr, "failed to create vulkan image views\n"); + return 0; + } else { + context->swapchain_image_views = image_views; + } + + VkRenderPass render_pass = create_render_pass(context->device, context->swapchain_format); + if(render_pass == VK_NULL_HANDLE) { + fprintf(stderr, "failed to create vulkan render pass\n"); + return 0; + } else { + context->render_pass = render_pass; + } + + VkFramebuffer* framebuffers = create_swapchain_framebuffers(context->device, context->swapchain_image_count, context->swapchain_image_views, context->render_pass, context->swapchain_extent); + if(framebuffers == 0) { + fprintf(stderr, "failed to create vulkan framebuffers\n"); + return 0; + } else { + context->swapchain_framebuffers = framebuffers; + } + + VkSemaphore ia_semaphore = create_semaphore(context->device, 0); + if(ia_semaphore == VK_NULL_HANDLE) { + fprintf(stderr, "failed to create vulkan image available semaphore\n"); + return 0; + } else { + context->image_available_semaphore = ia_semaphore; + } + + VkSemaphore rf_semaphore = create_semaphore(context->device, 0); + if(rf_semaphore == VK_NULL_HANDLE) { + fprintf(stderr, "failed to create vulkan render finished semaphore\n"); + return 0; + } else { + context->render_finished_semaphore = rf_semaphore; + } + + VkFence if_fence = create_fence(context->device, VK_FENCE_CREATE_SIGNALED_BIT); + if(if_fence == VK_NULL_HANDLE) { + fprintf(stderr, "failed to create vulkan in flight fence\n"); + return 0; + } else { + context->in_flight_fence = if_fence; + } + + VkCommandPool command_pool = create_command_pool(context->device, context->queue_indices.graphics_family); + if(command_pool == VK_NULL_HANDLE) { + fprintf(stderr, "failed to create vulkan graphics command pool"); + return 0; + } else { + context->graphics_command_pool = command_pool; + } + + VkPipelineLayout triangle_pipeline_layout = create_pipeline_layout(device, 0, 0, 0, 0); + if(triangle_pipeline_layout == VK_NULL_HANDLE) { + fprintf(stderr, "failed to create vulkan pipeline layout\n"); + return 0; + } else { + context->triangle_pipeline_layout = triangle_pipeline_layout; + } + + VkPipeline triangle_pipeline = create_graphics_pipeline(context->device, context->swapchain_extent, context->triangle_pipeline_layout, context->render_pass); + if(triangle_pipeline == VK_NULL_HANDLE) { + fprintf(stderr, "failed to create vulkan graphics pipeline\n"); + return 0; + } else { + context->triangle_pipeline = triangle_pipeline; + } + + VkCommandBuffer triangle_command_buffer = create_command_buffer(context->device, context->graphics_command_pool); + if(triangle_command_buffer == VK_NULL_HANDLE) { + fprintf(stderr, "failed to create vulkan triangle command buffer\n"); + return 0; + } else { + context->triangle_command_buffer = triangle_command_buffer; + } + return context; } -void main_loop(GLFWwindow* window) { +VkResult draw_frame(VulkanContext* context) { + VkResult result; + result = vkWaitForFences(context->device, 1, &context->in_flight_fence, VK_TRUE, UINT64_MAX); + if(result != VK_SUCCESS) { + return result; + } + + result = vkResetFences(context->device, 1, &context->in_flight_fence); + if(result != VK_SUCCESS) { + return result; + } + + uint32_t image_index; + result = vkAcquireNextImageKHR(context->device, context->swapchain, UINT64_MAX, context->image_available_semaphore, VK_NULL_HANDLE, &image_index); + if(result != VK_SUCCESS) { + return result; + } + + result = vkResetCommandBuffer(context->triangle_command_buffer, 0); + if(result != VK_SUCCESS) { + return result; + } + + result = record_command_buffer_triangle(context->triangle_command_buffer, image_index, context->render_pass, context->swapchain_framebuffers, context->swapchain_extent, context->triangle_pipeline); + if(result != VK_SUCCESS) { + return result; + } + + VkSubmitInfo submit_info = {}; + VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &context->image_available_semaphore; + submit_info.pWaitDstStageMask = wait_stages; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &context->triangle_command_buffer; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &context->render_finished_semaphore; + + result = vkQueueSubmit(context->queues.graphics, 1, &submit_info, context->in_flight_fence); + if(result != VK_SUCCESS) { + return result; + } + + VkPresentInfoKHR present_info = {}; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = &context->render_finished_semaphore; + present_info.swapchainCount = 1; + present_info.pSwapchains = &context->swapchain; + present_info.pImageIndices = &image_index; + present_info.pResults = 0; + + return vkQueuePresentKHR(context->queues.present, &present_info); +} + +void main_loop(GLFWwindow* window, VulkanContext* context) { while(!glfwWindowShouldClose(window)) { glfwPollEvents(); + draw_frame(context); } } @@ -603,7 +1200,7 @@ int main() { return 2; } - main_loop(window); + main_loop(window, context); cleanup(window, context);