wintoys

wintoys.git
git clone git://git.lenczewski.org/wintoys.git
Log | Files | Refs | README

vkdemo.c (25458B)


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