// Copyright 2019-2024, Collabora, Ltd.
// Copyright 2024-2026, NVIDIA CORPORATION.
// SPDX-License-Identifier: BSL-1.0
/*!
 * @file
 * @brief Functions to init various parts of the vk_bundle.
 *
 * Note that some functionality in this file is generated by the script
 * `src/xrt/auxiliary/vk/vk_generate_inc_files.py`. The generated parts are
 * included via .c.inc files.
 *
 * @author Jakob Bornecrantz <jakob@collabora.com>
 * @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
 * @author Moshi Turner <moshiturner@protonmail.com>
 * @author Korcan Hussein <korcan.hussein@collabora.com>
 * @ingroup aux_vk
 */

#include "util/u_pretty_print.h"

#include "vk/vk_compositor_flags.h"
#include "vk/vk_helpers.h"
#include "vk/vk_extensions_helpers.h"
#include "vk/vk_queue_builder.h"
#include "vk/vk_queue_family.h"

#include <stdio.h>


/*
 *
 * Extension skip policy callbacks.
 *
 */

static bool
should_skip_optional_instance_ext(struct vk_bundle *vk,
                                  struct u_extension_list *required_instance_ext_list,
                                  struct u_extension_list *optional_instance_ext_list,
                                  const char *ext)
{
#ifdef VK_EXT_display_surface_counter
	if (strcmp(ext, VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME) == 0) {
		// it does not make sense to enable surface counter on anything that does not use a VkDisplayKHR
		if (!u_extension_list_contains(required_instance_ext_list, VK_KHR_DISPLAY_EXTENSION_NAME)) {
			VK_DEBUG(vk, "Skipping optional instance extension %s because %s is not enabled", ext,
			         VK_KHR_DISPLAY_EXTENSION_NAME);
			return true;
		}
		VK_DEBUG(vk, "Not skipping optional instance extension %s because %s is enabled", ext,
		         VK_KHR_DISPLAY_EXTENSION_NAME);
	}
#endif
	return false;
}

static bool
should_skip_optional_device_ext(struct vk_bundle *vk,
                                struct u_extension_list *required_device_ext_list,
                                struct u_extension_list *optional_device_ext_list,
                                const char *ext)
{
#ifdef VK_EXT_display_control
	// only enable VK_EXT_display_control when we enabled VK_EXT_display_surface_counter instance ext
	if (strcmp(ext, VK_EXT_DISPLAY_CONTROL_EXTENSION_NAME) == 0) {
		if (!vk->has_EXT_display_surface_counter) {
			VK_DEBUG(vk, "Skipping optional instance extension %s because %s instance ext is not enabled",
			         ext, VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME);
			return true;
		}
		VK_DEBUG(vk, "Not skipping optional instance extension %s because %s instance ext is enabled", ext,
		         VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME);
	}
#endif
	return false;
}


/*
 *
 * 'Exported' instance functions.
 *
 */

VkResult
vk_build_instance_extensions(struct vk_bundle *vk,
                             struct u_extension_list *required_instance_ext_list,
                             struct u_extension_list *optional_instance_ext_list,
                             struct u_extension_list **out_instance_ext_list)
{
	return vk_build_instance_extensions_with_skip( //
	    vk,                                        //
	    required_instance_ext_list,                //
	    optional_instance_ext_list,                //
	    should_skip_optional_instance_ext,         //
	    out_instance_ext_list);                    //
}

VkResult
vk_check_required_instance_extensions(struct vk_bundle *vk, struct u_extension_list *required_instance_ext_list)
{
	VkExtensionProperties *props = NULL;
	uint32_t prop_count = 0;
	VkResult ret;

	// Two call.
	ret = vk_enumerate_instance_extensions_properties( //
	    vk,                                            // vk_bundle
	    NULL,                                          // layer_name
	    &prop_count,                                   // out_prop_count
	    &props);                                       // out_props
	if (ret != VK_SUCCESS) {
		return ret; // Already logged.
	}

	// Convert to extension list for easier checking.
	struct u_extension_list *available_ext_list = vk_convert_extension_properties_to_string_list(props, prop_count);

	// Clean up props.
	free(props);

	// Check if all required extensions are available.
	ret = vk_check_required_extensions(vk, available_ext_list, required_instance_ext_list, "instance");

	// Clean up.
	u_extension_list_destroy(&available_ext_list);

	return ret;
}

void
vk_fill_in_has_instance_extensions(struct vk_bundle *vk, struct u_extension_list *ext_list)
{
	// Include the generated extension detection code.
#include "vk_bundle_init_instance_ext.c.inc"
}


/*
 *
 * Physical device feature helpers.
 *
 */

static void
fill_in_device_features(struct vk_bundle *vk, const uint32_t queue_family_index)
{
	/*
	 * Device properties.
	 */

	VkPhysicalDeviceProperties pdp;
	vk->vkGetPhysicalDeviceProperties(vk->physical_device, &pdp);

	vk->features.timestamp_compute_and_graphics = pdp.limits.timestampComputeAndGraphics;
	vk->features.timestamp_period = pdp.limits.timestampPeriod;

	vk->limits.max_sampler_allocation_count = pdp.limits.maxSamplerAllocationCount;
	vk->limits.max_bound_descriptor_sets = pdp.limits.maxBoundDescriptorSets;
	vk->limits.max_descriptor_set_samplers = pdp.limits.maxDescriptorSetSamplers;
	vk->limits.max_descriptor_set_sampled_images = pdp.limits.maxDescriptorSetSampledImages;
	vk->limits.max_per_stage_descriptor_samplers = pdp.limits.maxPerStageDescriptorSamplers;
	vk->limits.max_per_stage_descriptor_sampled_images = pdp.limits.maxPerStageDescriptorSampledImages;
	vk->limits.max_per_stage_descriptor_storage_images = pdp.limits.maxPerStageDescriptorStorageImages;


	/*
	 * Queue properties.
	 */

	VkQueueFamilyProperties *props = NULL;
	uint32_t count = 0;

	vk_get_physical_device_queue_family_properties( //
	    vk,                                         //
	    vk->physical_device,                        //
	    &count,                                     //
	    &props);                                    //
	assert(count != 0);
	assert(count > queue_family_index);

	vk->features.timestamp_valid_bits = props[queue_family_index].timestampValidBits;
	free(props);
}

static void
get_external_image_support(struct vk_bundle *vk,
                           bool depth,
                           VkExternalMemoryHandleTypeFlagBits handle_type,
                           bool *out_importable,
                           bool *out_exportable)
{
	// Note that this is a heuristic: just picked two somewhat-random formats to test with here.
	// Before creating an actual swapchain we check the desired format for real.
	// Not using R16G16B16A16_UNORM because 8bpx linear is discouraged, and not using
	// the SRGB version because Android's AHardwareBuffer is weird with SRGB (no internal support)
	VkFormat image_format = depth ? VK_FORMAT_D16_UNORM : VK_FORMAT_R16G16B16A16_UNORM;
	enum xrt_swapchain_usage_bits bits =
	    depth ? (enum xrt_swapchain_usage_bits)(XRT_SWAPCHAIN_USAGE_DEPTH_STENCIL | XRT_SWAPCHAIN_USAGE_SAMPLED)
	          : (enum xrt_swapchain_usage_bits)(XRT_SWAPCHAIN_USAGE_COLOR | XRT_SWAPCHAIN_USAGE_SAMPLED);
	vk_csci_get_image_external_support(vk, image_format, bits, handle_type, out_importable, out_exportable);
}

static bool
is_fence_bit_supported(struct vk_bundle *vk, VkExternalFenceHandleTypeFlagBits handle_type)
{
	VkPhysicalDeviceExternalFenceInfo external_fence_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO,
	    .handleType = handle_type,
	};
	VkExternalFenceProperties external_fence_props = {
	    .sType = VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES,
	};

	vk->vkGetPhysicalDeviceExternalFencePropertiesKHR( //
	    vk->physical_device,                           // physicalDevice
	    &external_fence_info,                          // pExternalFenceInfo
	    &external_fence_props);                        // pExternalFenceProperties

	const VkExternalFenceFeatureFlagBits bits =    //
	    VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT | //
	    VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT;  //

	VkExternalFenceFeatureFlagBits masked = bits & external_fence_props.externalFenceFeatures;
	if (masked != bits) {
		// All must be supported.
		return false;
	}

	return true;
}

static void
get_binary_semaphore_bit_support(struct vk_bundle *vk,
                                 VkExternalSemaphoreHandleTypeFlagBits handle_type,
                                 bool *out_importable,
                                 bool *out_exportable)
{
	VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
	    .pNext = NULL,
	    .handleType = handle_type,
	};
	VkExternalSemaphoreProperties external_semaphore_props = {
	    .sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES,
	};

	vk->vkGetPhysicalDeviceExternalSemaphorePropertiesKHR( //
	    vk->physical_device,                               // physicalDevice
	    &external_semaphore_info,                          // pExternalSemaphoreInfo
	    &external_semaphore_props);                        // pExternalSemaphoreProperties

	const VkExternalSemaphoreFeatureFlagBits bits = external_semaphore_props.externalSemaphoreFeatures;

	*out_importable = (bits & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT) != 0;
	*out_exportable = (bits & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) != 0;
}

static bool
is_binary_semaphore_bit_supported(struct vk_bundle *vk, VkExternalSemaphoreHandleTypeFlagBits handle_type)
{
	bool importable = false, exportable = false;
	get_binary_semaphore_bit_support(vk, handle_type, &importable, &exportable);

	return importable && exportable;
}

static void
get_timeline_semaphore_bit_support(struct vk_bundle *vk,
                                   VkExternalSemaphoreHandleTypeFlagBits handle_type,
                                   bool *out_importable,
                                   bool *out_exportable)
{
	*out_importable = false;
	*out_exportable = false;

#ifdef VK_KHR_timeline_semaphore
	/*
	 * This technically is for the device not the physical device,
	 * but we can use it as a way to gate running the detection code.
	 */
	if (!vk->features.timeline_semaphore) {
		return;
	}

	VkSemaphoreTypeCreateInfo semaphore_type_create_info = {
	    .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
	    .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
	    .initialValue = 0,
	};
	VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
	    .pNext = (const void *)&semaphore_type_create_info,
	    .handleType = handle_type,
	};
	VkExternalSemaphoreProperties external_semaphore_props = {
	    .sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES,
	};

	vk->vkGetPhysicalDeviceExternalSemaphorePropertiesKHR( //
	    vk->physical_device,                               // physicalDevice
	    &external_semaphore_info,                          // pExternalSemaphoreInfo
	    &external_semaphore_props);                        // pExternalSemaphoreProperties

	const VkExternalSemaphoreFeatureFlagBits bits = external_semaphore_props.externalSemaphoreFeatures;

	*out_importable = (bits & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT) != 0;
	*out_exportable = (bits & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) != 0;
#endif
}

static bool
is_timeline_semaphore_bit_supported(struct vk_bundle *vk, VkExternalSemaphoreHandleTypeFlagBits handle_type)
{
	bool importable = false, exportable = false;
	get_timeline_semaphore_bit_support(vk, handle_type, &importable, &exportable);

	return importable && exportable;
}

static void
fill_in_external_object_properties(struct vk_bundle *vk)
{
	// Make sure it's cleared.
	U_ZERO(&vk->external);

	if (vk->vkGetPhysicalDeviceExternalFencePropertiesKHR == NULL) {
		VK_WARN(vk, "vkGetPhysicalDeviceExternalFencePropertiesKHR not supported, should always be.");
		return;
	}

	if (vk->vkGetPhysicalDeviceExternalSemaphorePropertiesKHR == NULL) {
		VK_WARN(vk, "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR not supported, should always be.");
		return;
	}
	if (vk->vkGetPhysicalDeviceImageFormatProperties2 == NULL) {
		VK_WARN(vk, "vkGetPhysicalDeviceImageFormatProperties2 not supported, should always be.");
		return;
	}

#if defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_WIN32_HANDLE)
	get_external_image_support(vk, false, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT,
	                           &vk->external.color_image_import_opaque_win32,
	                           &vk->external.color_image_export_opaque_win32);
	get_external_image_support(vk, true, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT,
	                           &vk->external.depth_image_import_opaque_win32,
	                           &vk->external.depth_image_export_opaque_win32);


	get_external_image_support(vk, false, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT,
	                           &vk->external.color_image_import_d3d11, &vk->external.color_image_export_d3d11);
	get_external_image_support(vk, true, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT,
	                           &vk->external.depth_image_import_d3d11, &vk->external.depth_image_export_d3d11);

#elif defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_FD)
	get_external_image_support(vk, false, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
	                           &vk->external.color_image_import_opaque_fd,
	                           &vk->external.color_image_export_opaque_fd);
	get_external_image_support(vk, true, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
	                           &vk->external.depth_image_import_opaque_fd,
	                           &vk->external.depth_image_export_opaque_fd);

#elif defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_AHARDWAREBUFFER)
	get_external_image_support(vk, false, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
	                           &vk->external.color_image_import_opaque_fd,
	                           &vk->external.color_image_export_opaque_fd);
	get_external_image_support(vk, true, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
	                           &vk->external.depth_image_import_opaque_fd,
	                           &vk->external.depth_image_export_opaque_fd);


	get_external_image_support(vk, false, VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
	                           &vk->external.color_image_import_ahardwarebuffer,
	                           &vk->external.color_image_export_ahardwarebuffer);
	get_external_image_support(vk, true, VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
	                           &vk->external.depth_image_import_ahardwarebuffer,
	                           &vk->external.depth_image_export_ahardwarebuffer);
#endif
#if defined(XRT_GRAPHICS_SYNC_HANDLE_IS_FD)

	vk->external.fence_sync_fd = is_fence_bit_supported( //
	    vk, VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT);
	vk->external.fence_opaque_fd = is_fence_bit_supported( //
	    vk, VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT);

	vk->external.binary_semaphore_sync_fd = is_binary_semaphore_bit_supported( //
	    vk, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT);
	vk->external.binary_semaphore_opaque_fd = is_binary_semaphore_bit_supported( //
	    vk, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT);

	//! @todo Is this safe to assume working, do we need to check an extension?
	vk->external.timeline_semaphore_sync_fd = is_timeline_semaphore_bit_supported( //
	    vk, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT);
	vk->external.timeline_semaphore_opaque_fd = is_timeline_semaphore_bit_supported( //
	    vk, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT);

#elif defined(XRT_GRAPHICS_SYNC_HANDLE_IS_WIN32_HANDLE)

	vk->external.fence_win32_handle = is_fence_bit_supported( //
	    vk, VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT);

	vk->external.binary_semaphore_d3d12_fence = is_binary_semaphore_bit_supported( //
	    vk, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT);
	vk->external.binary_semaphore_win32_handle = is_binary_semaphore_bit_supported( //
	    vk, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT);

	//! @todo Is this safe to assume working, do we need to check an extension?
	vk->external.timeline_semaphore_d3d12_fence = is_timeline_semaphore_bit_supported( //
	    vk, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT);
	vk->external.timeline_semaphore_win32_handle = is_timeline_semaphore_bit_supported( //
	    vk, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT);

#else
#error "Need port for fence sync handles checkers"
#endif
}


/*
 *
 * Device creation helper functions.
 *
 */

static int
device_type_priority(VkPhysicalDeviceType device_type)
{
	switch (device_type) {
	case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: return 4;
	case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return 3;
	case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: return 2;
	case VK_PHYSICAL_DEVICE_TYPE_CPU: return 1;
	default: return 0;
	}
}

static bool
device_is_preferred(VkPhysicalDeviceProperties *l_device, VkPhysicalDeviceProperties *r_device)
{
	int l_priority = device_type_priority(l_device->deviceType);
	int r_priority = device_type_priority(r_device->deviceType);

	if (l_priority > r_priority)
		return true;

	return false;
}

static void
device_debug_print(struct vk_bundle *vk, const VkPhysicalDeviceProperties *pdp, uint32_t index)
{
	char title[32];
	(void)snprintf(title, sizeof(title), "GPU index %u\n", index);
	vk_print_device_info(vk, U_LOGGING_DEBUG, pdp, index, title);
}

static uint32_t
select_preferred_device(struct vk_bundle *vk, VkPhysicalDevice *devices, uint32_t device_count)
{
	assert(device_count > 0);

	// Default to first if there is only one.
	uint32_t gpu_index = 0;
	VkPhysicalDeviceProperties gpu_properties;
	vk->vkGetPhysicalDeviceProperties(devices[0], &gpu_properties);

	// Loop starts at index 1, so print the first GPU here.
	device_debug_print(vk, &gpu_properties, 0);

	for (uint32_t i = 1; i < device_count; i++) {
		VkPhysicalDeviceProperties pdp;
		vk->vkGetPhysicalDeviceProperties(devices[i], &pdp);

		// Print GPU 1 to device_count here.
		device_debug_print(vk, &pdp, i);

		// Prefer devices based on device type priority, with preference to equal devices with smaller index
		if (device_is_preferred(&pdp, &gpu_properties)) {
			gpu_index = i;
			gpu_properties = pdp;
		}
	}

	return gpu_index;
}

static VkResult
select_physical_device(struct vk_bundle *vk, int forced_index)
{
	VkPhysicalDevice *physical_devices = NULL;
	uint32_t gpu_count = 0;
	VkResult ret;

	ret = vk_enumerate_physical_devices( //
	    vk,                              // vk_bundle
	    &gpu_count,                      // out_physical_device_count
	    &physical_devices);              // out_physical_devices
	if (ret != VK_SUCCESS) {
		VK_ERROR(vk, "vk_enumerate_physical_devices: %s", vk_result_string(ret));
		return ret;
	}
	if (gpu_count == 0) {
		VK_ERROR(vk, "No physical device found!");
		return VK_ERROR_DEVICE_LOST; // No need to free if zero devices.
	}

	VK_DEBUG(vk, "Choosing Vulkan device index");
	uint32_t gpu_index = 0;
	if (forced_index >= 0) {
		uint32_t uint_index = (uint32_t)forced_index;
		if (uint_index + 1 > gpu_count) {
			VK_ERROR(vk, "Attempted to force GPU index %u, but only %u GPUs are available", uint_index,
			         gpu_count);
			free(physical_devices);
			return VK_ERROR_DEVICE_LOST;
		}
		gpu_index = uint_index;
		VK_DEBUG(vk, "Forced use of Vulkan device index %u.", gpu_index);
	} else {
		VK_DEBUG(vk, "Available GPUs");
		gpu_index = select_preferred_device(vk, physical_devices, gpu_count);
	}

	// Setup the physical device on the bundle.
	vk->physical_device = physical_devices[gpu_index];
	vk->physical_device_index = gpu_index;

	// Free the array.
	free(physical_devices);
	physical_devices = NULL;


	/*
	 * Have now selected device, get properties of it.
	 */

	VkPhysicalDeviceProperties pdp;
	vk->vkGetPhysicalDeviceProperties(vk->physical_device, &pdp);

	char title[64];
	(void)snprintf(title, sizeof(title), "Selected GPU: %u\n", gpu_index);
	vk_print_device_info(vk, U_LOGGING_DEBUG, &pdp, gpu_index, title);

	char *tegra_substr = strstr(pdp.deviceName, "Tegra");
	if (tegra_substr) {
		vk->is_tegra = true;
		VK_DEBUG(vk, "Detected Tegra, using Tegra specific workarounds!");
	}

	// Fill out the device memory props as well.
	vk->vkGetPhysicalDeviceMemoryProperties(vk->physical_device, &vk->device_memory_props);

	return VK_SUCCESS;
}

static void
fill_in_has_device_extensions(struct vk_bundle *vk, struct u_extension_list *ext_list)
{
#include "vk_bundle_init_device_ext.c.inc"
}

/*!
 * @brief Sets fields in @p device_features to true if and only if they are available and they are true in @p
 * optional_device_features (indicating a desire for that feature)
 *
 * @param vk self
 * @param physical_device The physical device to query
 * @param[in] optional_device_features The features to request if available
 * @param[out] device_features Populated with the subset of @p optional_device_features that are actually available.
 */
static void
filter_device_features(struct vk_bundle *vk,
                       VkPhysicalDevice physical_device,
                       const struct vk_device_features *optional_device_features,
                       struct vk_device_features *device_features)
{
	// If no features are requested, then noop.
	if (optional_device_features == NULL) {
		return;
	}

	/*
	 * The structs
	 */

#ifdef VK_EXT_robustness2
	VkPhysicalDeviceRobustness2FeaturesEXT robust_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT,
	    .pNext = NULL,
	};
#endif

#ifdef VK_KHR_8bit_storage
	VkPhysicalDevice8BitStorageFeaturesKHR storage_8bit = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR,
	    .pNext = NULL,
	};
#endif

#ifdef VK_KHR_timeline_semaphore
	VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_semaphore_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR,
	    .pNext = NULL,
	};
#endif

#if defined(VK_KHR_present_id) && defined(VK_KHR_present_wait)
	VkPhysicalDevicePresentIdFeaturesKHR present_id_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR,
	    .pNext = NULL,
	};

	VkPhysicalDevicePresentWaitFeaturesKHR present_wait_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR,
	    .pNext = NULL,
	};
#endif

#ifdef VK_KHR_synchronization2
	VkPhysicalDeviceSynchronization2FeaturesKHR synchronization_2_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR,
	    .pNext = NULL,
	};
#endif

#ifdef VK_KHR_video_maintenance1
	VkPhysicalDeviceVideoMaintenance1FeaturesKHR video_maintenance_1_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_MAINTENANCE_1_FEATURES_KHR,
	    .pNext = NULL,
	};
#endif

#ifdef VK_ANDROID_external_format_resolve
	VkPhysicalDeviceExternalFormatResolveFeaturesANDROID ext_fmt_resolve_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FORMAT_RESOLVE_FEATURES_ANDROID,
	    .pNext = NULL,
	};
#endif

	VkPhysicalDeviceFeatures2 physical_device_features = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
	    .pNext = NULL,
	};

#ifdef VK_EXT_robustness2
	if (vk->has_EXT_robustness2) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&physical_device_features,
		                         (VkBaseInStructure *)&robust_info);
	}
#endif

#ifdef VK_KHR_8bit_storage
	if (vk->has_KHR_8bit_storage) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&physical_device_features,
		                         (VkBaseInStructure *)&storage_8bit);
	}
#endif

#ifdef VK_KHR_timeline_semaphore
	if (vk->has_KHR_timeline_semaphore) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&physical_device_features,
		                         (VkBaseInStructure *)&timeline_semaphore_info);
	}
#endif

#if defined(VK_KHR_present_id) && defined(VK_KHR_present_wait)
	if (vk->has_KHR_present_wait) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&physical_device_features,
		                         (VkBaseInStructure *)&present_id_info);

		vk_append_to_pnext_chain((VkBaseInStructure *)&physical_device_features,
		                         (VkBaseInStructure *)&present_wait_info);
	}
#endif

#ifdef VK_KHR_synchronization2
	if (vk->has_KHR_synchronization2) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&physical_device_features,
		                         (VkBaseInStructure *)&synchronization_2_info);
	}
#endif

#ifdef VK_KHR_video_maintenance1
	if (vk->has_KHR_video_maintenance1) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&physical_device_features,
		                         (VkBaseInStructure *)&video_maintenance_1_info);
	}
#endif

#ifdef VK_ANDROID_external_format_resolve
	if (vk->has_ANDROID_external_format_resolve) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&physical_device_features,
		                         (VkBaseInStructure *)&ext_fmt_resolve_info);
	}
#endif

	vk->vkGetPhysicalDeviceFeatures2( //
	    physical_device,              // physicalDevice
	    &physical_device_features);   // pFeatures


	/*
	 * Collect and transfer.
	 */

#define CHECK(feature, DEV_FEATURE) device_features->feature = optional_device_features->feature && (DEV_FEATURE)

#ifdef VK_EXT_robustness2
	CHECK(null_descriptor, robust_info.nullDescriptor);
#endif

#ifdef VK_KHR_8bit_storage
	CHECK(storage_buffer_8bit_access, storage_8bit.storageBuffer8BitAccess);
#endif

#ifdef VK_KHR_timeline_semaphore
	CHECK(timeline_semaphore, timeline_semaphore_info.timelineSemaphore);
#endif

#if defined(VK_KHR_present_id) && defined(VK_KHR_present_wait)
	// we need both extensions enabled/functional
	CHECK(present_wait, present_id_info.presentId && present_wait_info.presentWait);
#endif

#ifdef VK_KHR_synchronization2
	CHECK(synchronization_2, synchronization_2_info.synchronization2);
#endif

#ifdef VK_KHR_video_maintenance1
	CHECK(video_maintenance_1, video_maintenance_1_info.videoMaintenance1);
#endif

#ifdef VK_ANDROID_external_format_resolve
	CHECK(ext_fmt_resolve, ext_fmt_resolve_info.externalFormatResolve);
#endif

	CHECK(shader_image_gather_extended, physical_device_features.features.shaderImageGatherExtended);

	CHECK(shader_storage_image_write_without_format,
	      physical_device_features.features.shaderStorageImageWriteWithoutFormat);

#undef CHECK


	VK_DEBUG(vk,
	         "Features:"
	         "\n\text_fmt_resolve: %i"
	         "\n\tnull_descriptor: %i"
	         "\n\tshader_image_gather_extended: %i"
	         "\n\tshader_storage_image_write_without_format: %i"
	         "\n\tstorage_buffer_8bit_access: %i"
	         "\n\tsynchronization_2: %i"
	         "\n\ttimeline_semaphore: %i"
	         "\n\tvideo_maintenance_1: %i",                              //
	         device_features->ext_fmt_resolve,                           //
	         device_features->null_descriptor,                           //
	         device_features->shader_image_gather_extended,              //
	         device_features->shader_storage_image_write_without_format, //
	         device_features->storage_buffer_8bit_access,                //
	         device_features->synchronization_2,                         //
	         device_features->timeline_semaphore,                        //
	         device_features->video_maintenance_1);                      //
}

static inline void
vk_reset_queues(struct vk_bundle *vk)
{
	const struct vk_queue_pair null_queue_pair = VK_NULL_QUEUE_PAIR;

	for (uint32_t i = 0; i < ARRAY_SIZE(vk->queues); ++i) {
		struct vk_bundle_queue *q = &vk->queues[i];
		q->queue = VK_NULL_HANDLE;
		q->index = null_queue_pair.index;
		q->family_index = null_queue_pair.family_index;
	}
}

static struct vk_bundle_queue *
vk_insert_get_queue(struct vk_bundle *vk, const struct vk_queue_pair *new_queue)
{
	if (vk == NULL ||                       //
	    new_queue == NULL ||                //
	    new_queue->index == (uint32_t)-1 || //
	    new_queue->family_index == VK_QUEUE_FAMILY_IGNORED) {
		return NULL;
	}

	/*
	 * The same queue can be used for different uses, but we need to have
	 * one mutex per queue, so look up if the queue has already been added.
	 */
	for (uint32_t i = 0; i < VK_BUNDLE_MAX_QUEUES; ++i) {
		struct vk_bundle_queue *q = &vk->queues[i];
		if (q->queue != VK_NULL_HANDLE &&   //
		    new_queue->index == q->index && //
		    new_queue->family_index == q->family_index) {
			return q;
		}
	}

	// Find the first unused queue bundle.
	for (uint32_t i = 0; i < VK_BUNDLE_MAX_QUEUES; ++i) {
		struct vk_bundle_queue *q = &vk->queues[i];
		if (q->queue != VK_NULL_HANDLE) {
			continue;
		}

		q->index = new_queue->index;
		q->family_index = new_queue->family_index;

		vk->vkGetDeviceQueue(vk->device, q->family_index, q->index, &q->queue);
		return q;
	}

	VK_ERROR(vk, "failed to find a free queue, max queues reached");
	return NULL;
}


/*
 *
 * 'Exported' device functions.
 *
 */

VkResult
vk_select_physical_device(struct vk_bundle *vk, int forced_index)
{
	return select_physical_device(vk, forced_index);
}

XRT_CHECK_RESULT VkResult
vk_create_device(struct vk_bundle *vk,
                 int forced_index,
                 bool only_compute,
                 VkQueueGlobalPriorityEXT global_priority,
                 struct u_extension_list *required_device_ext_list,
                 struct u_extension_list *optional_device_ext_list,
                 const struct vk_device_features *optional_device_features)
{
	struct u_extension_list *device_ext_list = NULL;
	VkResult ret;

	ret = select_physical_device(vk, forced_index);
	VK_CHK_WITH_GOTO(ret, "select_physical_device", err_destroy);

	ret = vk_build_device_extensions_with_skip( //
	    vk,                                     //
	    vk->physical_device,                    //
	    required_device_ext_list,               //
	    optional_device_ext_list,               //
	    should_skip_optional_device_ext,        //
	    &device_ext_list);                      //
	VK_CHK_WITH_GOTO(ret, "vk_build_device_extensions_with_skip", err_destroy);

	// Fill in has_* fields based on the extension list.
	fill_in_has_device_extensions(vk, device_ext_list);


	/*
	 * Features
	 */

	struct vk_device_features device_features = {0};
	filter_device_features(vk, vk->physical_device, optional_device_features, &device_features);
	vk->features.timeline_semaphore = device_features.timeline_semaphore;
	vk->features.synchronization_2 = device_features.synchronization_2;
	vk->features.present_wait = device_features.present_wait;
	vk->features.video_maintenance_1 = device_features.video_maintenance_1;


	/*
	 * Queue
	 */

	// If we don't have global priority, only allow medium priority queues.
	if (!vk->has_EXT_global_priority && //
	    !vk->has_KHR_global_priority && //
	    global_priority != VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT) {
		ret = VK_ERROR_NOT_PERMITTED_EXT;
		goto err_destroy;
	}

	vk_reset_queues(vk);

	// To builde the queue create
	struct vk_queue_builder builder = {0};

	// Always get a graphics queue.
	struct vk_queue_family graphics_queue_family = {0};
	ret = vk_queue_family_find_graphics(vk, &graphics_queue_family);
	VK_CHK_WITH_GOTO(ret, "vk_queue_family_find_graphics", err_destroy);

	// In compute-only mode, get a separate compute queue as the main queue.
	struct vk_queue_pair main_queue = {0};
	if (only_compute) {
		struct vk_queue_family compute_queue_family = {0};
		ret = vk_queue_family_find_and_avoid_graphics(vk, VK_QUEUE_COMPUTE_BIT, &compute_queue_family);
		VK_CHK_WITH_GOTO(ret, "vk_queue_family_find_and_avoid_graphics", err_destroy);

		assert(compute_queue_family.queue_family.queueCount > 0);

		main_queue = vk_queue_builder_add(&builder, compute_queue_family.family_index);
	} else {
		assert(graphics_queue_family.queue_family.queueCount > 0);

		// In normal mode, main queue is the graphics queue.
		main_queue = vk_queue_builder_add(&builder, graphics_queue_family.family_index);
	}

	// Assume that the graphics queue family has at least one queue.
	assert(graphics_queue_family.queue_family.queueCount > 0);

	/*
	 * Do this after the main queue is added, so that it comes before
	 * graphics queue in the list of queues, this is important for the
	 * priority info. If graphics is main the add function will reuse the
	 * main queue index.
	 */
	const struct vk_queue_pair graphics_queue = vk_queue_builder_add(&builder, graphics_queue_family.family_index);

	VkDeviceQueueGlobalPriorityCreateInfoEXT priority_info = {
	    .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT,
	    .pNext = NULL,
	    .globalPriority = global_priority,
	};

#ifdef VK_KHR_video_encode_queue
	// Video encode queue
	struct vk_queue_pair encode_queue = VK_NULL_QUEUE_PAIR;
	if (u_extension_list_contains(device_ext_list, VK_KHR_VIDEO_ENCODE_QUEUE_EXTENSION_NAME)) {
		struct vk_queue_family encode_queue_family = {0};
		ret = vk_queue_family_find_and_avoid_graphics(vk, VK_QUEUE_VIDEO_ENCODE_BIT_KHR, &encode_queue_family);
		if (ret == VK_SUCCESS) {
			encode_queue = vk_queue_builder_add(&builder, encode_queue_family.family_index);
		}
	}
#endif

#ifdef VK_KHR_global_priority
	static_assert(VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT ==
	                  VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR,
	              "Vulkan structs doesn't have the same structure ID!");
#endif

	if (vk->has_EXT_global_priority || vk->has_KHR_global_priority) {
		// This is okay, see static_assert above.
		priority_info.pNext = builder.create_infos[0].pNext;
		builder.create_infos[0].pNext = (void *)&priority_info;
	}


	/*
	 * Device
	 */

#ifdef VK_EXT_robustness2
	VkPhysicalDeviceRobustness2FeaturesEXT robust_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT,
	    .pNext = NULL,
	    .nullDescriptor = device_features.null_descriptor,
	};
#endif

#ifdef VK_KHR_8bit_storage
	VkPhysicalDevice8BitStorageFeaturesKHR storage_8bit = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR,
	    .pNext = NULL,
	    .storageBuffer8BitAccess = device_features.storage_buffer_8bit_access,
	};
#endif

#if VK_KHR_present_wait
	VkPhysicalDevicePresentWaitFeaturesKHR present_wait = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR,
	    .pNext = NULL,
	    .presentWait = device_features.present_wait,
	};
#endif

#if VK_KHR_present_id
	VkPhysicalDevicePresentIdFeaturesKHR present_id = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR,
	    .pNext = NULL,
	    .presentId = device_features.present_wait,
	};
#endif

#ifdef VK_KHR_timeline_semaphore
	VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_semaphore_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR,
	    .pNext = NULL,
	    .timelineSemaphore = device_features.timeline_semaphore,
	};
#endif

#ifdef VK_KHR_synchronization2
	VkPhysicalDeviceSynchronization2FeaturesKHR synchronization_2_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR,
	    .pNext = NULL,
	    .synchronization2 = device_features.synchronization_2,
	};
#endif

#ifdef VK_KHR_video_maintenance1
	VkPhysicalDeviceVideoMaintenance1FeaturesKHR video_maintenance_1_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_MAINTENANCE_1_FEATURES_KHR,
	    .pNext = NULL,
	    .videoMaintenance1 = device_features.video_maintenance_1,
	};
#endif

#ifdef VK_ANDROID_external_format_resolve
	VkPhysicalDeviceExternalFormatResolveFeaturesANDROID ext_fmt_resolve_info = {
	    .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FORMAT_RESOLVE_FEATURES_ANDROID,
	    .pNext = NULL,
	    .externalFormatResolve = device_features.ext_fmt_resolve,
	};
#endif

	VkPhysicalDeviceFeatures enabled_features = {
	    .shaderImageGatherExtended = device_features.shader_image_gather_extended,
	    .shaderStorageImageWriteWithoutFormat = device_features.shader_storage_image_write_without_format,
	};

	VkDeviceCreateInfo device_create_info = {
	    .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
	    .queueCreateInfoCount = builder.create_info_count,
	    .pQueueCreateInfos = builder.create_infos,
	    .enabledExtensionCount = u_extension_list_get_size(device_ext_list),
	    .ppEnabledExtensionNames = u_extension_list_get_data(device_ext_list),
	    .pEnabledFeatures = &enabled_features,
	};

#ifdef VK_EXT_robustness2
	if (vk->has_EXT_robustness2) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&device_create_info, (VkBaseInStructure *)&robust_info);
	}
#endif

#ifdef VK_KHR_8bit_storage
	if (vk->has_KHR_8bit_storage) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&device_create_info, (VkBaseInStructure *)&storage_8bit);
	}
#endif

#ifdef VK_KHR_present_id
	if (vk->has_KHR_present_wait) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&device_create_info, (VkBaseInStructure *)&present_id);
	}
#endif

#ifdef VK_KHR_present_wait
	if (vk->has_KHR_present_wait) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&device_create_info, (VkBaseInStructure *)&present_wait);
	}
#endif

#ifdef VK_KHR_timeline_semaphore
	if (vk->has_KHR_timeline_semaphore) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&device_create_info,
		                         (VkBaseInStructure *)&timeline_semaphore_info);
	}
#endif

#ifdef VK_KHR_synchronization2
	if (vk->has_KHR_synchronization2) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&device_create_info,
		                         (VkBaseInStructure *)&synchronization_2_info);
	}
#endif

#ifdef VK_KHR_video_maintenance1
	if (vk->has_KHR_video_maintenance1) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&device_create_info,
		                         (VkBaseInStructure *)&video_maintenance_1_info);
	}
#endif

#ifdef VK_ANDROID_external_format_resolve
	if (vk->has_ANDROID_external_format_resolve) {
		vk_append_to_pnext_chain((VkBaseInStructure *)&device_create_info,
		                         (VkBaseInStructure *)&ext_fmt_resolve_info);
	}
#endif

	ret = vk->vkCreateDevice(vk->physical_device, &device_create_info, NULL, &vk->device);

	// Destroy the list now.
	u_extension_list_destroy(&device_ext_list);

	if (ret == VK_ERROR_NOT_PERMITTED_EXT) {
		// Don't spam the not permitted returns as we try all of the different priorities.
		VK_DEBUG(vk, "Is CAP_SYS_NICE set? Try: sudo setcap cap_sys_nice+ep monado-service");
		goto err_destroy;
	}
	VK_CHK_WITH_GOTO(ret, "vkCreateDevice", err_destroy);

	// Fill in the device features we are interested in.
	fill_in_device_features(vk, main_queue.family_index);

	// We fill in these here as we want to be sure we have selected the physical device fully.
	fill_in_external_object_properties(vk);

	// Now setup all of the device specific functions.
	ret = vk_get_device_functions(vk);
	VK_CHK_WITH_GOTO(ret, "vk_get_device_functions", err_destroy);

	vk->main_queue = vk_insert_get_queue(vk, &main_queue);
	assert(vk->main_queue != NULL);

	vk->graphics_queue = vk_insert_get_queue(vk, &graphics_queue);
	assert(vk->graphics_queue != NULL);

#if defined(VK_KHR_video_encode_queue)
	vk->encode_queue = vk_insert_get_queue(vk, &encode_queue);
#endif

	// Need to do this after functions have been gotten.
	VK_NAME_INSTANCE(vk, vk->instance, "vk_bundle instance");
	VK_NAME_DEVICE(vk, vk->device, "vk_bundle device");

	return ret;

err_destroy:
	if (vk->device != VK_NULL_HANDLE) {
		vk->vkDestroyDevice(vk->device, NULL);
		vk->device = VK_NULL_HANDLE;
	}

	// Safe to call even if null.
	u_extension_list_destroy(&device_ext_list);

	return ret;
}

VkResult
vk_init_mutex(struct vk_bundle *vk)
{
	assert(vk != NULL);

	for (uint32_t i = 0; i < ARRAY_SIZE(vk->queues); ++i) {
		struct vk_bundle_queue *q = &vk->queues[i];

		if (os_mutex_init(&q->mutex) < 0) {
			VK_ERROR(vk, "failed to initialize queue mutex");
			return VK_ERROR_INITIALIZATION_FAILED;
		}
	}
	return VK_SUCCESS;
}

VkResult
vk_deinit_mutex(struct vk_bundle *vk)
{
	assert(vk != NULL);

	for (uint32_t i = 0; i < ARRAY_SIZE(vk->queues); ++i) {
		struct vk_bundle_queue *q = &vk->queues[i];
		os_mutex_destroy(&q->mutex);
		q->mutex = (struct os_mutex){0};
	}
	return VK_SUCCESS;
}

/*
 *
 * Complete setup.
 *
 */

XRT_CHECK_RESULT VkResult
vk_init_from_given(struct vk_bundle *vk,
                   PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr,
                   VkInstance instance,
                   VkPhysicalDevice physical_device,
                   VkDevice device,
                   uint32_t queue_family_index,
                   uint32_t queue_index,
                   bool external_fence_fd_enabled,
                   bool external_semaphore_fd_enabled,
                   bool timeline_semaphore_enabled,
                   bool image_format_list_enabled,
                   bool debug_utils_enabled,
                   enum u_logging_level log_level)
{
	VkResult ret;

	// First memset it clear.
	U_ZERO(vk);
	vk->log_level = log_level;

	vk_reset_queues(vk);

	ret = vk_get_loader_functions(vk, vkGetInstanceProcAddr);
	if (ret != VK_SUCCESS) {
		goto err_memset;
	}

	vk->instance = instance;
	vk->physical_device = physical_device;
	vk->device = device;

	// Fill in all instance functions.
	ret = vk_get_instance_functions(vk);
	if (ret != VK_SUCCESS) {
		goto err_memset;
	}

	// Fill out the device memory props here, as we are
	// passed a vulkan context and do not call selectPhysicalDevice()
	vk->vkGetPhysicalDeviceMemoryProperties(vk->physical_device, &vk->device_memory_props);

	// Vulkan does not let us read what extensions was enabled.
	if (external_fence_fd_enabled) {
		vk->has_KHR_external_fence_fd = true;
	}

	// Vulkan does not let us read what extensions was enabled.
	if (external_semaphore_fd_enabled) {
		vk->has_KHR_external_semaphore_fd = true;
	}

	// Vulkan does not let us read what extensions was enabled.
	if (image_format_list_enabled) {
		vk->has_KHR_image_format_list = image_format_list_enabled;
	}

#ifdef VK_KHR_timeline_semaphore
	/*
	 * Has the timeline semaphore extension and feature been enabled?
	 * Need to do this before fill_in_external_object_properties.
	 */
	if (timeline_semaphore_enabled) {
		vk->has_KHR_timeline_semaphore = true;
		vk->features.timeline_semaphore = true;
	}
#endif

	const struct vk_queue_pair main_queue = {
	    .family_index = queue_family_index,
	    .index = queue_index,
	};
	// Fill in the device features we are interested in.
	fill_in_device_features(vk, main_queue.family_index);

	// Fill in external object properties.
	fill_in_external_object_properties(vk);

	// Fill in all device functions.
	ret = vk_get_device_functions(vk);
	if (ret != VK_SUCCESS) {
		goto err_memset;
	}

	vk->main_queue = vk_insert_get_queue(vk, &main_queue);
	assert(vk->main_queue != NULL);

	// In vk_init_from_given, we don't know if the queue is graphics or not.
	// Assume it's graphics-capable as a best guess.
	vk->graphics_queue = vk->main_queue;

	vk->has_EXT_debug_utils = false;
	if (debug_utils_enabled) {
#ifdef VK_EXT_debug_utils
		if (vk->vkSetDebugUtilsObjectNameEXT) {
			vk->has_EXT_debug_utils = true;
		} else {
			VK_WARN(vk, "EXT_debug_utils requested but extension is not loaded");
		}
#else
		VK_WARN(vk, "EXT_debug_utils requested but extension was not available at compile time");
#endif
	}

	return VK_SUCCESS;

err_memset:
	U_ZERO(vk);
	return ret;
}
