wl-graphics-demo

wl-graphics-demo.git
git clone git://git.lenczewski.org/wl-graphics-demo.git
Log | Files | Refs | README

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 }