vulkan.c (24376B)
1 static uint32_t vert_shader[] = 2 #include "shader.vert.spv.inc" 3 ; 4 5 static uint32_t frag_shader[] = 6 #include "shader.frag.spv.inc" 7 ; 8 9 struct queue_family_indices { 10 uint32_t graphics_family; 11 bool has_graphics_family; 12 13 uint32_t present_family; 14 bool has_present_family; 15 }; 16 17 static struct queue_family_indices 18 get_device_queue_families(VkPhysicalDevice device, VkSurfaceKHR surface) 19 { 20 struct queue_family_indices result = {0}; 21 22 uint32_t queue_family_count; 23 vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, NULL); 24 25 VkQueueFamilyProperties *queue_families = alloca(queue_family_count * sizeof *queue_families); 26 vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families); 27 28 for (uint32_t i = 0; i < queue_family_count; i++) { 29 if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { 30 result.has_graphics_family = true; 31 result.graphics_family = i; 32 } 33 34 VkBool32 present_support = false; 35 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &present_support); 36 if (present_support) { 37 result.has_present_family = true; 38 result.present_family = i; 39 } 40 41 if (result.has_graphics_family && result.has_present_family) 42 break; 43 } 44 45 return result; 46 } 47 48 static bool 49 is_device_suitable(VkPhysicalDevice device, VkSurfaceKHR surface, const char **device_extensions, uint32_t device_extensions_len) 50 { 51 struct queue_family_indices indices = get_device_queue_families(device, surface); 52 53 // NOTE: we require a device to have both graphics and present capability for its queue 54 if (!indices.has_graphics_family || !indices.has_present_family) 55 return false; 56 57 uint32_t extension_count; 58 vkEnumerateDeviceExtensionProperties(device, NULL, &extension_count, NULL); 59 60 VkExtensionProperties *extensions = alloca(extension_count * sizeof *extensions); 61 vkEnumerateDeviceExtensionProperties(device, NULL, &extension_count, extensions); 62 63 for (uint32_t i = 0; i < device_extensions_len; i++) { 64 for (uint32_t j = 0; j < extension_count; j++) { 65 if (strcmp(device_extensions[i], extensions[j].extensionName) == 0) 66 goto extension_found; 67 } 68 69 // NOTE: device does not support a required extension 70 return false; 71 72 extension_found: 73 continue; 74 } 75 76 uint32_t format_count; 77 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &format_count, NULL); 78 if (!format_count) return false; 79 80 uint32_t present_mode_count; 81 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_mode_count, NULL); 82 if (!present_mode_count) return false; 83 84 return true; 85 } 86 87 static VkExtent2D 88 get_swapchain_extent(VkSurfaceCapabilitiesKHR *surface_caps, uint32_t width, uint32_t height) 89 { 90 if (surface_caps->currentExtent.width == UINT32_MAX && surface_caps->currentExtent.height == UINT32_MAX) { 91 return (VkExtent2D) { 92 .width = CLAMP(width, surface_caps->minImageExtent.width, surface_caps->maxImageExtent.width), 93 .height = CLAMP(height, surface_caps->minImageExtent.height, surface_caps->maxImageExtent.height), 94 }; 95 } else { 96 return surface_caps->currentExtent; 97 } 98 } 99 100 static VkSurfaceFormatKHR 101 get_swapchain_format(VkSurfaceFormatKHR *formats, uint64_t len) 102 { 103 for (uint64_t i = 0; i < len; i++) { 104 if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB && formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) 105 return formats[i]; 106 } 107 108 return formats[0]; 109 } 110 111 static VkPresentModeKHR 112 get_swapchain_present_mode(VkPresentModeKHR *present_modes, uint64_t len) 113 { 114 for (uint64_t i = 0; i < len; i++) { 115 if (present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) 116 return present_modes[i]; 117 } 118 119 return VK_PRESENT_MODE_FIFO_KHR; 120 } 121 122 static bool 123 recreate_swapchain(struct window *window, uint32_t width, uint32_t height, VkSwapchainKHR old_swapchain) 124 { 125 struct queue_family_indices indices = get_device_queue_families(window->vk.physical_device, window->vk.surface); 126 assert(indices.has_graphics_family && indices.has_present_family); 127 128 VkSurfaceCapabilitiesKHR surface_caps; 129 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(window->vk.physical_device, window->vk.surface, &surface_caps); 130 131 uint32_t format_count; 132 vkGetPhysicalDeviceSurfaceFormatsKHR(window->vk.physical_device, window->vk.surface, &format_count, NULL); 133 134 assert(format_count); 135 VkSurfaceFormatKHR *formats = alloca(format_count * sizeof *formats); 136 vkGetPhysicalDeviceSurfaceFormatsKHR(window->vk.physical_device, window->vk.surface, &format_count, formats); 137 138 uint32_t present_mode_count; 139 vkGetPhysicalDeviceSurfacePresentModesKHR(window->vk.physical_device, window->vk.surface, &present_mode_count, NULL); 140 141 assert(present_mode_count); 142 VkPresentModeKHR *present_modes = alloca(present_mode_count * sizeof *present_modes); 143 vkGetPhysicalDeviceSurfacePresentModesKHR(window->vk.physical_device, window->vk.surface, &present_mode_count, present_modes); 144 145 VkExtent2D extent = get_swapchain_extent(&surface_caps, width, height); 146 VkSurfaceFormatKHR format = get_swapchain_format(formats, format_count); 147 VkPresentModeKHR present_mode = get_swapchain_present_mode(present_modes, present_mode_count); 148 149 uint32_t image_count = surface_caps.minImageCount + 1; 150 if (surface_caps.maxImageCount) 151 image_count = MIN(image_count, surface_caps.maxImageCount); 152 153 assert(indices.graphics_family == indices.present_family); 154 VkSwapchainCreateInfoKHR swapchain_info = { 155 .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, 156 .surface = window->vk.surface, 157 .minImageCount = image_count, 158 .imageFormat = format.format, 159 .imageColorSpace = format.colorSpace, 160 .imageExtent = extent, 161 .imageArrayLayers = 1, 162 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 163 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, 164 .preTransform = surface_caps.currentTransform, 165 .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 166 .presentMode = present_mode, 167 .clipped = VK_TRUE, 168 .oldSwapchain = old_swapchain, 169 }; 170 171 window->vk.swapchain_image_format = format.format; 172 window->vk.swapchain_extent = extent; 173 174 if (vkCreateSwapchainKHR(window->vk.device, &swapchain_info, NULL, &window->vk.swapchain) != VK_SUCCESS) 175 return false; 176 177 vkGetSwapchainImagesKHR(window->vk.device, window->vk.swapchain, &image_count, NULL); 178 179 window->vk.swapchain_images.len = image_count; 180 window->vk.swapchain_image_views.len = image_count; 181 window->vk.swapchain_framebuffers.len = image_count; 182 window->vk.swapchain_submit_semaphores.len = image_count; 183 184 window->vk.swapchain_images.ptr = malloc(image_count * sizeof *window->vk.swapchain_images.ptr); 185 assert(window->vk.swapchain_images.ptr); 186 187 vkGetSwapchainImagesKHR(window->vk.device, window->vk.swapchain, &window->vk.swapchain_images.len, window->vk.swapchain_images.ptr); 188 189 window->vk.swapchain_image_views.ptr = malloc(image_count * sizeof *window->vk.swapchain_image_views.ptr); 190 assert(window->vk.swapchain_image_views.ptr); 191 192 window->vk.swapchain_framebuffers.ptr = malloc(window->vk.swapchain_framebuffers.len * sizeof *window->vk.swapchain_framebuffers.ptr); 193 assert(window->vk.swapchain_framebuffers.ptr); 194 195 window->vk.swapchain_submit_semaphores.ptr = malloc(image_count * sizeof *window->vk.swapchain_submit_semaphores.ptr); 196 assert(window->vk.swapchain_submit_semaphores.ptr); 197 198 VkSemaphoreCreateInfo semaphore_info = { 199 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, 200 }; 201 202 for (uint32_t i = 0; i < window->vk.swapchain_submit_semaphores.len; i++) { 203 if (vkCreateSemaphore(window->vk.device, &semaphore_info, NULL, &window->vk.swapchain_submit_semaphores.ptr[i]) != VK_SUCCESS) { 204 fprintf(stderr, "Failed to create swapchain image sync objects!\n"); 205 exit(EXIT_FAILURE); 206 } 207 } 208 209 for (uint32_t i = 0; i < window->vk.swapchain_images.len; i++) { 210 VkImageViewCreateInfo image_view_info = { 211 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 212 .image = window->vk.swapchain_images.ptr[i], 213 .viewType = VK_IMAGE_VIEW_TYPE_2D, 214 .format = window->vk.swapchain_image_format, 215 .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, 216 .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, 217 .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, 218 .components.a = VK_COMPONENT_SWIZZLE_IDENTITY, 219 .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 220 .subresourceRange.baseMipLevel = 0, 221 .subresourceRange.levelCount = 1, 222 .subresourceRange.baseArrayLayer = 0, 223 .subresourceRange.layerCount = 1, 224 }; 225 226 if (vkCreateImageView(window->vk.device, &image_view_info, NULL, &window->vk.swapchain_image_views.ptr[i]) != VK_SUCCESS) 227 return false; 228 } 229 230 VkAttachmentDescription color_attachment = { 231 .format = window->vk.swapchain_image_format, 232 .samples = VK_SAMPLE_COUNT_1_BIT, 233 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, 234 .storeOp = VK_ATTACHMENT_STORE_OP_STORE, 235 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, 236 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, 237 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, 238 .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 239 }; 240 241 VkAttachmentReference color_attachment_ref = { 242 .attachment = 0, 243 .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 244 }; 245 246 VkSubpassDescription subpass = { 247 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, 248 .colorAttachmentCount = 1, 249 .pColorAttachments = &color_attachment_ref, 250 }; 251 252 VkSubpassDependency dependency = { 253 .srcSubpass = VK_SUBPASS_EXTERNAL, 254 .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 255 .srcAccessMask = 0, 256 .dstSubpass = 0, 257 .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 258 .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 259 }; 260 261 VkRenderPassCreateInfo render_pass_info = { 262 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, 263 .attachmentCount = 1, 264 .pAttachments = &color_attachment, 265 .subpassCount = 1, 266 .pSubpasses = &subpass, 267 .dependencyCount = 1, 268 .pDependencies = &dependency, 269 }; 270 271 if (vkCreateRenderPass(window->vk.device, &render_pass_info, NULL, &window->vk.render_pass) != VK_SUCCESS) 272 return false; 273 274 for (uint32_t i = 0; i < window->vk.swapchain_framebuffers.len; i++) { 275 VkImageView attachments[] = { window->vk.swapchain_image_views.ptr[i], }; 276 277 VkFramebufferCreateInfo framebuffer_info = { 278 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, 279 .renderPass = window->vk.render_pass, 280 .attachmentCount = ARRLEN(attachments), 281 .pAttachments = attachments, 282 .width = window->vk.swapchain_extent.width, 283 .height = window->vk.swapchain_extent.height, 284 .layers = 1, 285 }; 286 287 if (vkCreateFramebuffer(window->vk.device, &framebuffer_info, NULL, &window->vk.swapchain_framebuffers.ptr[i]) != VK_SUCCESS) 288 return false; 289 } 290 291 return true; 292 } 293 294 static void 295 destroy_swapchain(struct window *window) 296 { 297 // TODO: can this cause deadlock? 298 vkDeviceWaitIdle(window->vk.device); 299 300 for (uint32_t i = 0; i < window->vk.swapchain_submit_semaphores.len; i++) { 301 vkDestroySemaphore(window->vk.device, window->vk.swapchain_submit_semaphores.ptr[i], NULL); 302 } 303 304 free(window->vk.swapchain_submit_semaphores.ptr); 305 306 for (uint32_t i = 0; i < window->vk.swapchain_framebuffers.len; i++) { 307 vkDestroyFramebuffer(window->vk.device, window->vk.swapchain_framebuffers.ptr[i], NULL); 308 } 309 310 free(window->vk.swapchain_framebuffers.ptr); 311 312 vkDestroyRenderPass(window->vk.device, window->vk.render_pass, NULL); 313 314 for (uint32_t i = 0; i < window->vk.swapchain_image_views.len; i++) { 315 vkDestroyImageView(window->vk.device, window->vk.swapchain_image_views.ptr[i], NULL); 316 } 317 318 free(window->vk.swapchain_image_views.ptr); 319 320 // TODO: is this safe? 321 free(window->vk.swapchain_images.ptr); 322 323 vkDestroySwapchainKHR(window->vk.device, window->vk.swapchain, NULL); 324 } 325 326 static bool 327 create_shader_module(VkDevice device, uint32_t *code, uint64_t size, VkShaderModule *out) 328 { 329 VkShaderModuleCreateInfo module_info = { 330 .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, 331 .codeSize = size, 332 .pCode = code, 333 }; 334 335 return vkCreateShaderModule(device, &module_info, NULL, out) == VK_SUCCESS; 336 } 337 338 static bool 339 create_pipeline(struct window *window) 340 { 341 VkShaderModule vert_shader_module; 342 if (!create_shader_module(window->vk.device, vert_shader, sizeof vert_shader, &vert_shader_module)) { 343 fprintf(stderr, "Failed to create vertex shader module!\n"); 344 return false; 345 } 346 347 VkShaderModule frag_shader_module; 348 if (!create_shader_module(window->vk.device, frag_shader, sizeof frag_shader, &frag_shader_module)) { 349 fprintf(stderr, "Failed to create fragment shader module!\n"); 350 return false; 351 } 352 353 VkPipelineShaderStageCreateInfo vert_stage_info = { 354 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 355 .stage = VK_SHADER_STAGE_VERTEX_BIT, 356 .module = vert_shader_module, 357 .pName = "main", 358 }; 359 360 VkPipelineShaderStageCreateInfo frag_stage_info = { 361 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 362 .stage = VK_SHADER_STAGE_FRAGMENT_BIT, 363 .module = frag_shader_module, 364 .pName = "main", 365 }; 366 367 VkPipelineShaderStageCreateInfo shader_stages[] = { 368 vert_stage_info, 369 frag_stage_info, 370 }; 371 372 VkDynamicState dynamic_states[] = { 373 VK_DYNAMIC_STATE_VIEWPORT, 374 VK_DYNAMIC_STATE_SCISSOR, 375 }; 376 377 VkPipelineDynamicStateCreateInfo dynamic_state_info = { 378 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, 379 .dynamicStateCount = ARRLEN(dynamic_states), 380 .pDynamicStates = dynamic_states, 381 }; 382 383 VkPipelineVertexInputStateCreateInfo vertex_input_info = { 384 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, 385 .vertexBindingDescriptionCount = 0, 386 .pVertexBindingDescriptions = NULL, 387 .vertexAttributeDescriptionCount = 0, 388 .pVertexAttributeDescriptions = NULL, 389 }; 390 391 VkPipelineInputAssemblyStateCreateInfo input_assembly_info = { 392 .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, 393 .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 394 .primitiveRestartEnable = VK_FALSE, 395 }; 396 397 VkPipelineViewportStateCreateInfo viewport_info = { 398 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, 399 .viewportCount = 1, 400 .scissorCount = 1, 401 }; 402 403 VkPipelineRasterizationStateCreateInfo rasteriser_info = { 404 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, 405 .depthClampEnable = VK_FALSE, 406 .rasterizerDiscardEnable = VK_FALSE, 407 .polygonMode = VK_POLYGON_MODE_FILL, 408 .lineWidth = 1.0f, 409 .cullMode = VK_CULL_MODE_BACK_BIT, 410 .frontFace = VK_FRONT_FACE_CLOCKWISE, 411 .depthBiasEnable = VK_FALSE, 412 }; 413 414 VkPipelineMultisampleStateCreateInfo multisample_info = { 415 .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, 416 .sampleShadingEnable = VK_FALSE, 417 .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, 418 }; 419 420 VkPipelineColorBlendAttachmentState color_blend_attachment = { 421 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, 422 .blendEnable = VK_FALSE, 423 }; 424 425 VkPipelineColorBlendStateCreateInfo color_blend_info = { 426 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 427 .logicOpEnable = VK_FALSE, 428 .attachmentCount = 1, 429 .pAttachments = &color_blend_attachment, 430 }; 431 432 VkPipelineLayoutCreateInfo pipeline_layout_info = { 433 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 434 }; 435 436 if (vkCreatePipelineLayout(window->vk.device, &pipeline_layout_info, NULL, &window->vk.pipeline_layout) != VK_SUCCESS) { 437 fprintf(stderr, "Failed to create pipeline layout!\n"); 438 return false; 439 } 440 441 VkGraphicsPipelineCreateInfo pipeline_info = { 442 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 443 .stageCount = 2, 444 .pStages = shader_stages, 445 .pVertexInputState = &vertex_input_info, 446 .pInputAssemblyState = &input_assembly_info, 447 .pViewportState = &viewport_info, 448 .pRasterizationState = &rasteriser_info, 449 .pMultisampleState = &multisample_info, 450 .pColorBlendState = &color_blend_info, 451 .pDynamicState = &dynamic_state_info, 452 .layout = window->vk.pipeline_layout, 453 .renderPass = window->vk.render_pass, 454 .subpass = 0, 455 .basePipelineHandle = VK_NULL_HANDLE, 456 .basePipelineIndex = -1, 457 }; 458 459 VkPipeline pipeline; 460 if (vkCreateGraphicsPipelines(window->vk.device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &pipeline) != VK_SUCCESS) { 461 fprintf(stderr, "Failed to create pipeline!\n"); 462 return false; 463 } 464 465 window->vk.pipeline = pipeline; 466 467 vkDestroyShaderModule(window->vk.device, frag_shader_module, NULL); 468 vkDestroyShaderModule(window->vk.device, vert_shader_module, NULL); 469 470 return true; 471 } 472 473 void 474 vk_init(struct window *window) 475 { 476 window->vk.frame_index = 0; 477 478 uint32_t physical_device_count; 479 vkEnumeratePhysicalDevices(window->vk.instance, &physical_device_count, NULL); 480 481 if (!physical_device_count) { 482 fprintf(stderr, "Failed to find physical devices supporting Vulkan 1.0!\n"); 483 exit(EXIT_FAILURE); 484 } 485 486 VkPhysicalDevice *physical_devices = alloca(physical_device_count * sizeof *physical_devices); 487 vkEnumeratePhysicalDevices(window->vk.instance, &physical_device_count, physical_devices); 488 489 const char *device_extensions[] = { 490 VK_KHR_SWAPCHAIN_EXTENSION_NAME, 491 }; 492 493 window->vk.physical_device = VK_NULL_HANDLE; 494 for (uint32_t i = 0; i < physical_device_count; i++) { 495 if (is_device_suitable(physical_devices[i], window->vk.surface, device_extensions, ARRLEN(device_extensions))) 496 window->vk.physical_device = physical_devices[i]; 497 } 498 499 if (window->vk.physical_device == VK_NULL_HANDLE) { 500 fprintf(stderr, "Failed to find suitable physical device!\n"); 501 exit(EXIT_FAILURE); 502 } 503 504 struct queue_family_indices device_queue_families = get_device_queue_families(window->vk.physical_device, window->vk.surface); 505 506 float queue_priority = 1.0f; 507 508 // TODO: handle case where graphics queue and present queue are separate 509 VkDeviceQueueCreateInfo queue_info = { 510 .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, 511 .queueFamilyIndex = device_queue_families.graphics_family, 512 .queueCount = 1, 513 .pQueuePriorities = &queue_priority, 514 }; 515 516 VkPhysicalDeviceFeatures device_features = {0}; 517 518 VkDeviceCreateInfo device_info = { 519 .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, 520 .pQueueCreateInfos = &queue_info, 521 .queueCreateInfoCount = 1, 522 .pEnabledFeatures = &device_features, 523 .enabledExtensionCount = ARRLEN(device_extensions), 524 .ppEnabledExtensionNames = device_extensions, 525 }; 526 527 if (vkCreateDevice(window->vk.physical_device, &device_info, NULL, &window->vk.device) != VK_SUCCESS) { 528 fprintf(stderr, "Failed to create logical device!\n"); 529 exit(EXIT_FAILURE); 530 } 531 532 vkGetDeviceQueue(window->vk.device, device_queue_families.graphics_family, 0, &window->vk.graphics_queue); 533 vkGetDeviceQueue(window->vk.device, device_queue_families.present_family, 0, &window->vk.present_queue); 534 535 if (!recreate_swapchain(window, window->width, window->height, VK_NULL_HANDLE)) { 536 fprintf(stderr, "Failed to create swapchain!\n"); 537 exit(EXIT_FAILURE); 538 } 539 540 if (!create_pipeline(window)) { 541 fprintf(stderr, "Failed to create graphics pipeline!\n"); 542 exit(EXIT_FAILURE); 543 } 544 545 VkCommandPoolCreateInfo command_pool_info = { 546 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 547 .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, 548 .queueFamilyIndex = device_queue_families.graphics_family, 549 }; 550 551 if (vkCreateCommandPool(window->vk.device, &command_pool_info, NULL, &window->vk.command_pool) != VK_SUCCESS) { 552 fprintf(stderr, "Failed to create command pool!\n"); 553 exit(EXIT_FAILURE); 554 } 555 556 VkCommandBufferAllocateInfo command_buffer_alloc_info = { 557 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 558 .commandPool = window->vk.command_pool, 559 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, 560 .commandBufferCount = ARRLEN(window->vk.command_buffers), 561 }; 562 563 if (vkAllocateCommandBuffers(window->vk.device, &command_buffer_alloc_info, window->vk.command_buffers) != VK_SUCCESS) { 564 fprintf(stderr, "Failed to allocate command buffers!\n"); 565 exit(EXIT_FAILURE); 566 } 567 568 VkSemaphoreCreateInfo semaphore_info = { 569 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, 570 }; 571 572 VkFenceCreateInfo fence_info = { 573 .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, 574 .flags = VK_FENCE_CREATE_SIGNALED_BIT, 575 }; 576 577 for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 578 if (vkCreateSemaphore(window->vk.device, &semaphore_info, NULL, &window->vk.sync_objects.image_available[i]) != VK_SUCCESS || 579 vkCreateFence(window->vk.device, &fence_info, NULL, &window->vk.sync_objects.in_flight[i]) != VK_SUCCESS) { 580 fprintf(stderr, "Failed to create sync objects!\n"); 581 exit(EXIT_FAILURE); 582 } 583 } 584 } 585 586 void 587 vk_draw(struct window *window) 588 { 589 uint32_t frame = window->vk.frame_index; 590 window->vk.frame_index = (frame + 1) % MAX_FRAMES_IN_FLIGHT; 591 592 VkFence frame_fence = window->vk.sync_objects.in_flight[frame]; 593 vkWaitForFences(window->vk.device, 1, &frame_fence, VK_TRUE, UINT64_MAX); 594 595 uint32_t image_index; 596 VkSemaphore acquire_semaphore = window->vk.sync_objects.image_available[frame]; 597 VkResult result = vkAcquireNextImageKHR(window->vk.device, window->vk.swapchain, UINT64_MAX, acquire_semaphore, VK_NULL_HANDLE, &image_index); 598 599 if (result == VK_ERROR_OUT_OF_DATE_KHR) { 600 destroy_swapchain(window); 601 if (!recreate_swapchain(window, window->width, window->height, VK_NULL_HANDLE)) 602 fprintf(stderr, "Failed to recreate swapchain!\n"); 603 return; 604 } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { 605 fprintf(stderr, "Failed to aqcuire swapchain image!\n"); 606 return; 607 } 608 609 vkResetFences(window->vk.device, 1, &frame_fence); 610 611 { 612 VkCommandBuffer buffer = window->vk.command_buffers[frame]; 613 vkResetCommandBuffer(buffer, 0); 614 615 VkCommandBufferBeginInfo begin_info = { 616 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 617 }; 618 619 if (vkBeginCommandBuffer(buffer, &begin_info) != VK_SUCCESS) { 620 fprintf(stderr, "Failed to begin recording command buffer!\n"); 621 return; 622 } 623 624 VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; 625 VkRenderPassBeginInfo render_pass_begin_info = { 626 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, 627 .renderPass = window->vk.render_pass, 628 .framebuffer = window->vk.swapchain_framebuffers.ptr[image_index], 629 .renderArea.offset = {0, 0}, 630 .renderArea.extent = window->vk.swapchain_extent, 631 .clearValueCount = 1, 632 .pClearValues = &clear_color, 633 }; 634 635 vkCmdBeginRenderPass(buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); 636 637 vkCmdBindPipeline(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, window->vk.pipeline); 638 639 VkViewport viewport = { 640 .x = 0.0f, 641 .y = 0.0f, 642 .width = (float) window->vk.swapchain_extent.width, 643 .height = (float) window->vk.swapchain_extent.height, 644 .minDepth = 0.0f, 645 .maxDepth = 1.0f, 646 }; 647 648 vkCmdSetViewport(buffer, 0, 1, &viewport); 649 650 VkRect2D scissor = { 651 .offset = {0, 0}, 652 .extent = window->vk.swapchain_extent, 653 }; 654 655 vkCmdSetScissor(buffer, 0, 1, &scissor); 656 657 uint32_t vertex_count = 3, instance_count = 1, first_vertex = 0, first_instance = 0; 658 vkCmdDraw(buffer, vertex_count, instance_count, first_vertex, first_instance); 659 660 vkCmdEndRenderPass(buffer); 661 662 if (vkEndCommandBuffer(buffer) != VK_SUCCESS) { 663 fprintf(stderr, "Failed to end recording command buffer!\n"); 664 return; 665 } 666 } 667 668 VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; 669 670 VkSemaphore submit_semaphore = window->vk.swapchain_submit_semaphores.ptr[image_index]; 671 672 VkSubmitInfo submit_info = { 673 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, 674 .waitSemaphoreCount = 1, 675 .pWaitSemaphores = &acquire_semaphore, 676 .pWaitDstStageMask = wait_stages, 677 .signalSemaphoreCount = 1, 678 .pSignalSemaphores = &submit_semaphore, 679 .commandBufferCount = 1, 680 .pCommandBuffers = &window->vk.command_buffers[frame], 681 }; 682 683 if (vkQueueSubmit(window->vk.graphics_queue, 1, &submit_info, frame_fence) != VK_SUCCESS) { 684 fprintf(stderr, "Failed to submit command buffer!\n"); 685 return; 686 } 687 688 VkPresentInfoKHR present_info = { 689 .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 690 .waitSemaphoreCount = 1, 691 .pWaitSemaphores = &submit_semaphore, 692 .swapchainCount = 1, 693 .pSwapchains = &window->vk.swapchain, 694 .pImageIndices = &image_index, 695 }; 696 697 vkQueuePresentKHR(window->vk.present_queue, &present_info); 698 } 699 700 void 701 vk_free(struct window *window) 702 { 703 vkDeviceWaitIdle(window->vk.device); 704 705 destroy_swapchain(window); 706 707 vkDestroyPipeline(window->vk.device, window->vk.pipeline, NULL); 708 vkDestroyPipelineLayout(window->vk.device, window->vk.pipeline_layout, NULL); 709 710 for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 711 vkDestroySemaphore(window->vk.device, window->vk.sync_objects.image_available[i], NULL); 712 vkDestroyFence(window->vk.device, window->vk.sync_objects.in_flight[i], NULL); 713 } 714 715 vkDestroyCommandPool(window->vk.device, window->vk.command_pool, NULL); 716 717 vkDestroyDevice(window->vk.device, NULL); 718 }