module; #define VK_NO_PROTOTYPES #define VK_USE_PLATFORM_XLIB_KHR #include #include #include #if defined(_WIN32) #error "Unsupported platform" #elif defined(__unix__) #include #endif export module renderer.backend.vk.library_wrapper; import memory.null_on_move; import math.vec3; import debug.assertions; import std; template struct overloads: Ts... { using Ts::operator()...; }; export namespace lt::renderer::vk { using Bool32 = VkBool32; namespace constants { constexpr auto application_version = VK_MAKE_VERSION(1, 0, 0); constexpr auto engine_version = VK_MAKE_VERSION(1, 0, 0); constexpr auto api_version = VK_API_VERSION_1_4; constexpr auto engine_name = std::string_view { "light_engine_vulkan_renderer" }; constexpr auto max_physical_device_name = VK_MAX_PHYSICAL_DEVICE_NAME_SIZE; constexpr auto max_memory_types = VK_MAX_MEMORY_TYPES; constexpr auto max_memory_heaps = VK_MAX_MEMORY_HEAPS; constexpr auto uuid_size = VK_UUID_SIZE; } // namespace constants namespace instance_layer_names { constexpr auto validation = "VK_LAYER_KHRONOS_validation"; } namespace instance_extension_names { constexpr auto debug_utils = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; constexpr auto surface = VK_KHR_SURFACE_EXTENSION_NAME; constexpr auto xlib_surface = VK_KHR_XLIB_SURFACE_EXTENSION_NAME; constexpr auto physical_device_properties_2 = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; } // namespace instance_extension_names namespace device_extension_names { constexpr auto xlib_surface = VK_KHR_XLIB_SURFACE_EXTENSION_NAME; constexpr auto swapchain = VK_KHR_SWAPCHAIN_EXTENSION_NAME; constexpr auto dynamic_rendering = VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME; constexpr auto descriptor_indexing = VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME; }; // namespace device_extension_names void load_library(); void unload_library(); void load_global_functions(); void load_device_functions(VkDevice device); using Version_T = uint32_t; struct ApplicationInfo { std::string_view name; Version_T version; std::string_view engine_name; Version_T engine_version; Version_T api_version; }; namespace QueueFlags { enum T : std::underlying_type_t // NOLINT { graphics_bit = VK_QUEUE_GRAPHICS_BIT, compute_bit = VK_QUEUE_COMPUTE_BIT, transfer_bit = VK_QUEUE_TRANSFER_BIT, sparse_binding_bit = VK_QUEUE_SPARSE_BINDING_BIT, protected_bit = VK_QUEUE_PROTECTED_BIT, video_decode_bit_khr = VK_QUEUE_VIDEO_DECODE_BIT_KHR, video_encode_bit_khr = VK_QUEUE_VIDEO_ENCODE_BIT_KHR, optical_flow_bit_nv = VK_QUEUE_OPTICAL_FLOW_BIT_NV, }; } namespace MemoryHeapFlags { enum T : std::underlying_type_t // NOLINT { device_local_bit = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT, multi_instance_bit = VK_MEMORY_HEAP_MULTI_INSTANCE_BIT, tile_memory_bit = VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM, }; }; namespace MemoryPropertyFlags { enum T : std::underlying_type_t // NOLINT { device_local_bit = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, host_visible_bit = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, host_coherent_bit = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, host_cached_bit = VK_MEMORY_PROPERTY_HOST_CACHED_BIT, lazily_allocated_bit = VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, protected_bit = VK_MEMORY_PROPERTY_PROTECTED_BIT, device_coherent_bit_amd = VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD, device_uncached_bit_amd = VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD, rdma_capable_bit_nv = VK_MEMORY_PROPERTY_RDMA_CAPABLE_BIT_NV, }; } class Instance { public: friend class Surface; friend class Gpu; struct Layer { struct Setting { std::string name; std::variant, std::uint32_t, bool> values; }; std::string name; std::vector settings; }; using Extension = std::string; struct CreateInfo { ApplicationInfo application_info; std::vector layers; std::vector extensions; }; Instance() = default; Instance(CreateInfo info); Instance(Instance &&) = default; Instance(const Instance &) = delete; auto operator=(Instance &&) -> Instance & = default; auto operator=(const Instance &) = delete; ~Instance(); void load_functions(); [[nodiscard]] operator bool() const { return m_instance != VK_NULL_HANDLE; } private: memory::NullOnMove m_instance {}; }; class Surface { public: friend class Gpu; struct XlibCreateInfo { Display *display; Window window; }; Surface() = default; Surface(const Instance &instance, const XlibCreateInfo &info); Surface(Surface &&) = default; Surface(const Surface &) = delete; auto operator=(Surface &&) -> Surface & = default; auto operator=(const Surface &) -> Surface & = delete; ~Surface(); [[nodiscard]] operator bool() const { return m_surface != VK_NULL_HANDLE; } private: memory::NullOnMove m_surface {}; VkInstance m_instance {}; }; class Gpu { public: enum class Type : uint8_t { other = 0, integrated_gpu = 1, discrete_gpu = 2, virtual_gpu = 3, cpu = 4, }; struct Features { Bool32 robust_buffer_access; Bool32 full_draw_index_uint32; Bool32 image_cube_array; Bool32 independent_blend; Bool32 geometry_shader; Bool32 tessellation_shader; Bool32 sample_rate_shading; Bool32 dual_src_blend; Bool32 logic_op; Bool32 multi_draw_indirect; Bool32 draw_indirect_first_instance; Bool32 depth_clamp; Bool32 depth_bias_clamp; Bool32 fill_mode_non_solid; Bool32 depth_bounds; Bool32 wide_lines; Bool32 large_points; Bool32 alpha_to_one; Bool32 multi_viewport; Bool32 sampler_anisotropy; Bool32 texture_compression_etc2; Bool32 texture_compression_astc_ldr; Bool32 texture_compression_bc; Bool32 occlusion_query_precise; Bool32 pipeline_statistics_query; Bool32 vertex_pipeline_stores_and_atomics; Bool32 fragment_stores_and_atomics; Bool32 shader_tessellation_and_geometry_point_size; Bool32 shader_image_gather_extended; Bool32 shader_storage_image_extended_formats; Bool32 shader_storage_image_multisample; Bool32 shader_storage_image_read_without_format; Bool32 shader_storage_image_write_without_format; Bool32 shader_uniform_buffer_array_dynamic_indexing; Bool32 shader_sampled_image_array_dynamic_indexing; Bool32 shader_storage_buffer_array_dynamic_indexing; Bool32 shader_storage_image_array_dynamic_indexing; Bool32 shader_clip_distance; Bool32 shader_cull_distance; Bool32 shader_float64; Bool32 shader_int64; Bool32 shader_int16; Bool32 shader_resource_residency; Bool32 shader_resource_min_lod; Bool32 sparse_binding; Bool32 sparse_residency_buffer; Bool32 sparse_residency_image_2d; Bool32 sparse_residency_image_3d; Bool32 sparse_residency_2_samples; Bool32 sparse_residency_4_samples; Bool32 sparse_residency_8_samples; Bool32 sparse_residency_16_samples; Bool32 sparse_residency_aliased; Bool32 variable_multisample_rate; Bool32 inherited_queries; }; struct Limits { std::uint32_t max_image_dimension_1d; std::uint32_t max_image_dimension_2d; std::uint32_t max_image_dimension_3d; std::uint32_t max_image_dimension_cube; std::uint32_t max_image_array_layers; std::uint32_t max_texel_buffer_elements; std::uint32_t max_uniform_buffer_range; std::uint32_t max_storage_buffer_range; std::uint32_t max_push_constants_size; std::uint32_t max_memory_allocation_count; std::uint32_t max_sampler_allocation_count; std::size_t buffer_image_granularity; std::size_t sparse_address_space_size; std::uint32_t max_bound_descriptor_sets; std::uint32_t max_per_stage_descriptor_samplers; std::uint32_t max_per_stage_descriptor_uniform_buffers; std::uint32_t max_per_stage_descriptor_storage_buffers; std::uint32_t max_per_stage_descriptor_sampled_images; std::uint32_t max_per_stage_descriptor_storage_images; std::uint32_t max_per_stage_descriptor_input_attachments; std::uint32_t max_per_stage_resources; std::uint32_t max_descriptor_set_samplers; std::uint32_t max_descriptor_set_uniform_buffers; std::uint32_t max_descriptor_set_uniform_buffers_dynamic; std::uint32_t max_descriptor_set_storage_buffers; std::uint32_t max_descriptor_set_storage_buffers_dynamic; std::uint32_t max_descriptor_set_sampled_images; std::uint32_t max_descriptor_set_storage_images; std::uint32_t max_descriptor_set_input_attachments; std::uint32_t max_vertex_input_attributes; std::uint32_t max_vertex_input_bindings; std::uint32_t max_vertex_input_attribute_offset; std::uint32_t max_vertex_input_binding_stride; std::uint32_t max_vertex_output_components; std::uint32_t max_tessellation_generation_level; std::uint32_t max_tessellation_patch_size; std::uint32_t max_tessellation_control_per_vertex_input_components; std::uint32_t max_tessellation_control_per_vertex_output_components; std::uint32_t max_tessellation_control_per_patch_output_components; std::uint32_t max_tessellation_control_total_output_components; std::uint32_t max_tessellation_evaluation_input_components; std::uint32_t max_tessellation_evaluation_output_components; std::uint32_t max_geometry_shader_invocations; std::uint32_t max_geometry_input_components; std::uint32_t max_geometry_output_components; std::uint32_t max_geometry_output_vertices; std::uint32_t max_geometry_total_output_components; std::uint32_t max_fragment_input_components; std::uint32_t max_fragment_output_attachments; std::uint32_t max_fragment_dual_src_attachments; std::uint32_t max_fragment_combined_output_resources; std::uint32_t max_compute_shared_memory_size; std::array max_compute_work_group_count; std::uint32_t max_compute_work_group_invocations; std::array max_compute_work_group_size; std::uint32_t sub_pixel_precision_bits; std::uint32_t sub_texel_precision_bits; std::uint32_t mipmap_precision_bits; std::uint32_t max_draw_indexed_index_value; std::uint32_t max_draw_indirect_count; float max_sampler_lod_bias; float max_sampler_anisotropy; std::uint32_t max_viewports; std::array max_viewport_dimensions; std::array viewport_bounds_range; std::uint32_t viewport_sub_pixel_bits; std::size_t min_memory_map_alignment; VkDeviceSize min_texel_buffer_offset_alignment; VkDeviceSize min_uniform_buffer_offset_alignment; VkDeviceSize min_storage_buffer_offset_alignment; std::int32_t min_texel_offset; std::uint32_t max_texel_offset; std::int32_t min_texel_gather_offset; std::uint32_t max_texel_gather_offset; float min_interpolation_offset; float max_interpolation_offset; std::uint32_t sub_pixel_interpolation_offset_bits; std::uint32_t max_framebuffer_width; std::uint32_t max_framebuffer_height; std::uint32_t max_framebuffer_layers; VkSampleCountFlags framebuffer_color_sample_counts; VkSampleCountFlags framebuffer_depth_sample_counts; VkSampleCountFlags framebuffer_stencil_sample_counts; VkSampleCountFlags framebuffer_no_attachments_sample_counts; std::uint32_t max_color_attachments; VkSampleCountFlags sampled_image_color_sample_counts; VkSampleCountFlags sampled_image_integer_sample_counts; VkSampleCountFlags sampled_image_depth_sample_counts; VkSampleCountFlags sampled_image_stencil_sample_counts; VkSampleCountFlags storage_image_sample_counts; std::uint32_t max_sample_mask_words; Bool32 timestamp_compute_and_graphics; float timestamp_period; std::uint32_t max_clip_distances; std::uint32_t max_cull_distances; std::uint32_t max_combined_clip_and_cull_distances; std::uint32_t discrete_queue_priorities; std::array point_size_range; std::array line_width_range; float point_size_granularity; float line_width_granularity; Bool32 strict_lines; Bool32 standard_sample_locations; std::size_t optimal_buffer_copy_offset_alignment; std::size_t optimal_buffer_copy_row_pitch_alignment; std::size_t non_coherent_atom_size; }; struct SparseProperties { Bool32 residency_standard_2d_block_shape; Bool32 residency_standard_2d_multisample_block_shape; Bool32 residency_standard_3d_block_shape; Bool32 residency_aligned_mip_size; Bool32 residency_non_resident_strict; }; struct Properties { std::uint32_t api_version; std::uint32_t driver_version; std::uint32_t vendor_id; std::uint32_t device_id; Type device_type; std::array device_name; std::array pipeline_cache_uuid; Limits limits; SparseProperties sparse_properties; }; struct MemoryType { MemoryPropertyFlags::T property_flags; std::uint32_t heap_idx; }; struct MemoryHeap { std::size_t size; MemoryHeapFlags::T flags; }; struct MemoryProperties { std::uint32_t memory_type_count; std::array memory_types; std::uint32_t memory_heap_count; std::array memory_heaps; }; struct QueueFamilyProperties { QueueFlags::T queue_flags {}; std::uint32_t queue_count {}; std::uint32_t timestamp_valid_bits {}; math::uvec3 min_image_transfer_granularity; }; [[nodiscard]] static auto enumerate(const Instance &instance) -> std::vector; Gpu() = default; Gpu(Gpu &&) = default; Gpu(const Gpu &) = default; auto operator=(Gpu &&) -> Gpu & = default; auto operator=(const Gpu &) -> Gpu & = default; ~Gpu(); [[nodiscard]] auto get_properties() const -> Properties; [[nodiscard]] auto get_features() const -> Features; [[nodiscard]] auto get_memory_properties() const -> MemoryProperties; [[nodiscard]] auto get_queue_family_properties() const -> std::vector; [[nodiscard]] auto queue_family_supports_surface( const Surface &surface, std::uint32_t queue_family_idx ) const -> bool; [[nodiscard]] operator bool() const { return m_physical_device != VK_NULL_HANDLE; } private: VkPhysicalDevice m_physical_device {}; VkInstance m_instance {}; }; class Device { public: struct CreateInfo { std::set queue_indices; std::vector extensions; Gpu::Features required_gpu_features; }; Device() = default; Device(CreateInfo info); Device(Device &&) = default; Device(const Device &) = delete; auto operator=(Device &&) -> Device & = default; auto operator=(const Device &) -> Device & = delete; ~Device(); private: memory::NullOnMove m_device {}; }; [[nodiscard]] auto enumerate_instance_extension_properties() -> std::vector; } // namespace lt::renderer::vk module :private; using namespace lt::renderer::vk; // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) // global functions PFN_vkGetInstanceProcAddr vk_get_instance_proc_address {}; PFN_vkCreateInstance vk_create_instance {}; PFN_vkEnumerateInstanceExtensionProperties vk_enumerate_instance_extension_properties {}; PFN_vkEnumerateInstanceLayerProperties vk_enumerate_instance_layer_properties {}; // instance functions PFN_vkDestroyInstance vk_destroy_instance {}; PFN_vkEnumeratePhysicalDevices vk_enumerate_physical_devices {}; PFN_vkGetPhysicalDeviceProperties vk_get_physical_device_properties {}; PFN_vkGetPhysicalDeviceQueueFamilyProperties vk_get_physical_device_queue_family_properties {}; PFN_vkCreateDevice vk_create_device {}; PFN_vkGetDeviceProcAddr vk_get_device_proc_address {}; PFN_vkDestroyDevice vk_destroy_device {}; PFN_vkGetPhysicalDeviceFeatures2 vk_get_physical_device_features {}; PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties {}; PFN_vkGetPhysicalDeviceMemoryProperties vk_get_physical_device_memory_properties {}; // extension instance functions PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label {}; PFN_vkCmdEndDebugUtilsLabelEXT vk_cmd_end_debug_label {}; PFN_vkCmdInsertDebugUtilsLabelEXT vk_cmd_insert_debug_label {}; PFN_vkCreateDebugUtilsMessengerEXT vk_create_debug_messenger {}; PFN_vkDestroyDebugUtilsMessengerEXT vk_destroy_debug_messenger {}; PFN_vkQueueBeginDebugUtilsLabelEXT vk_queue_begin_debug_label {}; PFN_vkQueueEndDebugUtilsLabelEXT vk_queue_end_debug_label {}; PFN_vkQueueInsertDebugUtilsLabelEXT vk_queue_insert_debug_label {}; PFN_vkSetDebugUtilsObjectNameEXT vk_set_debug_object_name {}; PFN_vkSetDebugUtilsObjectTagEXT vk_set_debug_object_tag {}; PFN_vkSubmitDebugUtilsMessageEXT vk_submit_debug_message {}; // surface instance functions PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support {}; PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities {}; PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats {}; // device functions PFN_vkGetDeviceQueue vk_get_device_queue {}; PFN_vkCreateCommandPool vk_create_command_pool {}; PFN_vkDestroyCommandPool vk_destroy_command_pool {}; PFN_vkAllocateCommandBuffers vk_allocate_command_buffers {}; PFN_vkFreeCommandBuffers vk_free_command_buffers {}; PFN_vkBeginCommandBuffer vk_begin_command_buffer {}; PFN_vkEndCommandBuffer vk_end_command_buffer {}; PFN_vkCmdPipelineBarrier vk_cmd_pipeline_barrier {}; PFN_vkQueueSubmit vk_queue_submit {}; PFN_vkQueueWaitIdle vk_queue_wait_idle {}; PFN_vkDeviceWaitIdle vk_device_wait_idle {}; PFN_vkCreateFence vk_create_fence {}; PFN_vkDestroyFence vk_destroy_fence {}; PFN_vkWaitForFences vk_wait_for_fences {}; PFN_vkResetFences vk_reset_fences {}; PFN_vkCreateSemaphore vk_create_semaphore {}; PFN_vkDestroySemaphore vk_destroy_semaphore {}; PFN_vkCreateSwapchainKHR vk_create_swapchain_khr {}; PFN_vkDestroySwapchainKHR vk_destroy_swapchain_khr {}; PFN_vkGetSwapchainImagesKHR vk_get_swapchain_images_khr {}; PFN_vkAcquireNextImageKHR vk_acquire_next_image_khr {}; PFN_vkQueuePresentKHR vk_queue_present_khr {}; PFN_vkCreateImageView vk_create_image_view {}; PFN_vkDestroyImageView vk_destroy_image_view {}; PFN_vkCreateRenderPass vk_create_render_pass {}; PFN_vkDestroyRenderPass vk_destroy_render_pass {}; PFN_vkCreateFramebuffer vk_create_frame_buffer {}; PFN_vkDestroyFramebuffer vk_destroy_frame_buffer {}; PFN_vkCreateShaderModule vk_create_shader_module {}; PFN_vkDestroyShaderModule vk_destroy_shader_module {}; PFN_vkCreatePipelineLayout vk_create_pipeline_layout {}; PFN_vkDestroyPipelineLayout vk_destroy_pipeline_layout {}; PFN_vkCreateGraphicsPipelines vk_create_graphics_pipelines {}; PFN_vkDestroyPipeline vk_destroy_pipeline {}; PFN_vkCmdBeginRenderPass vk_cmd_begin_render_pass {}; PFN_vkCmdEndRenderPass vk_cmd_end_render_pass {}; PFN_vkCmdBindPipeline vk_cmd_bind_pipeline {}; PFN_vkCmdDraw vk_cmd_draw {}; PFN_vkCmdSetViewport vk_cmd_set_viewport {}; PFN_vkCmdSetScissor vk_cmd_set_scissors {}; PFN_vkCmdPushConstants vk_cmd_push_constants {}; PFN_vkCmdCopyBuffer vk_cmd_copy_buffer {}; PFN_vkCreateDescriptorSetLayout vk_create_descriptor_set_layout {}; PFN_vkDestroyDescriptorSetLayout vk_destroy_descriptor_set_layout {}; PFN_vkCreateDescriptorPool vk_create_descriptor_pool {}; PFN_vkDestroyDescriptorPool vk_destroy_descriptor_pool {}; PFN_vkAllocateDescriptorSets vk_allocate_descriptor_sets {}; PFN_vkFreeDescriptorSets vk_free_descriptor_sets {}; PFN_vkCreateBuffer vk_create_buffer {}; PFN_vkDestroyBuffer vk_destroy_buffer {}; PFN_vkGetBufferMemoryRequirements vk_get_buffer_memory_requirements {}; PFN_vkAllocateMemory vk_allocate_memory {}; PFN_vkBindBufferMemory vk_bind_buffer_memory {}; PFN_vkMapMemory vk_map_memory {}; PFN_vkUnmapMemory vk_unmap_memory {}; PFN_vkFreeMemory vk_free_memory {}; PFN_vkResetCommandBuffer vk_reset_command_buffer {}; PFN_vkCmdBeginRendering vk_cmd_begin_rendering {}; PFN_vkCmdEndRendering vk_cmd_end_rendering {}; PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr {}; PFN_vkDestroySurfaceKHR vk_destroy_surface_khr {}; // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) namespace { void *library = nullptr; // NOLINT } void load_library() { constexpr auto runtime_loader_flags = RTLD_NOW | RTLD_LOCAL | RTLD_NODELETE; library = dlopen("libvulkan.so.1", runtime_loader_flags); if (!library) { library = dlopen("libvulkan.so", runtime_loader_flags); } lt::debug::ensure(library, "Failed to dlopen vulkan library"); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) vk_get_instance_proc_address = reinterpret_cast( dlsym(library, "vkGetInstanceProcAddr") ); lt::debug::ensure( vk_get_instance_proc_address, "Failed to load vulkan function: vkGetInstanceProcAddr" ); } void vkc(VkResult result) { if (result) { throw std::runtime_error { std::format("Vulkan call failed with result: {}", std::to_underlying(result)) }; } } void unload_library() { if (!library) { return; } // calling dlclose causes many issues with runtime analyzers // eg. https://github.com/google/sanitizers/issues/89 // with no noticable gains, so we just don't bother closing it. // dlclose(library); // library = nullptr; } void load_global_functions() { constexpr auto load_fn = [](T &pfn, const char *fn_name) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) pfn = reinterpret_cast(vk_get_instance_proc_address(nullptr, fn_name)); lt::debug::ensure(pfn, "Failed to load vulkan global function: {}", fn_name); // log::trace("Loaded global function: {}", fn_name); }; load_fn(vk_create_instance, "vkCreateInstance"); load_fn(vk_enumerate_instance_extension_properties, "vkEnumerateInstanceExtensionProperties"); load_fn(vk_enumerate_instance_layer_properties, "vkEnumerateInstanceLayerProperties"); } void Instance::load_functions() { const auto load_fn = [this](T &pfn, const char *fn_name) { pfn = std::bit_cast(vk_get_instance_proc_address(m_instance, fn_name)); lt::debug::ensure(pfn, "Failed to load vulkan instance function: {}", fn_name); }; load_fn(vk_destroy_instance, "vkDestroyInstance"); load_fn(vk_enumerate_physical_devices, "vkEnumeratePhysicalDevices"); load_fn(vk_get_physical_device_properties, "vkGetPhysicalDeviceProperties"); load_fn( vk_get_physical_device_queue_family_properties, "vkGetPhysicalDeviceQueueFamilyProperties" ); load_fn(vk_create_device, "vkCreateDevice"); load_fn(vk_get_device_proc_address, "vkGetDeviceProcAddr"); load_fn(vk_destroy_device, "vkDestroyDevice"); load_fn(vk_get_physical_device_features, "vkGetPhysicalDeviceFeatures"); load_fn(vk_enumerate_device_extension_properties, "vkEnumerateDeviceExtensionProperties"); load_fn(vk_get_physical_device_memory_properties, "vkGetPhysicalDeviceMemoryProperties"); load_fn(vk_cmd_begin_debug_label, "vkCmdBeginDebugUtilsLabelEXT"); load_fn(vk_cmd_end_debug_label, "vkCmdEndDebugUtilsLabelEXT"); load_fn(vk_cmd_insert_debug_label, "vkCmdInsertDebugUtilsLabelEXT"); load_fn(vk_create_debug_messenger, "vkCreateDebugUtilsMessengerEXT"); load_fn(vk_destroy_debug_messenger, "vkDestroyDebugUtilsMessengerEXT"); load_fn(vk_queue_begin_debug_label, "vkQueueBeginDebugUtilsLabelEXT"); load_fn(vk_queue_end_debug_label, "vkQueueEndDebugUtilsLabelEXT"); load_fn(vk_queue_insert_debug_label, "vkQueueInsertDebugUtilsLabelEXT"); load_fn(vk_set_debug_object_name, "vkSetDebugUtilsObjectNameEXT"); load_fn(vk_set_debug_object_tag, "vkSetDebugUtilsObjectTagEXT"); load_fn(vk_submit_debug_message, "vkSubmitDebugUtilsMessageEXT"); load_fn(vk_get_physical_device_surface_support, "vkGetPhysicalDeviceSurfaceSupportKHR"); load_fn( vk_get_physical_device_surface_capabilities, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" ); load_fn(vk_get_physical_device_surface_formats, "vkGetPhysicalDeviceSurfaceFormatsKHR"); load_fn(vk_create_xlib_surface_khr, "vkCreateXlibSurfaceKHR"); load_fn(vk_destroy_surface_khr, "vkDestroySurfaceKHR"); } void load_device_functions(VkDevice device) { const auto load_fn = [device](T &pfn, const char *fn_name) { pfn = std::bit_cast(vk_get_device_proc_address(device, fn_name)); lt::debug::ensure(pfn, "Failed to load vulkan device function: {}", fn_name); }; load_fn(vk_get_device_queue, "vkGetDeviceQueue"); load_fn(vk_create_command_pool, "vkCreateCommandPool"); load_fn(vk_destroy_command_pool, "vkDestroyCommandPool"); load_fn(vk_allocate_command_buffers, "vkAllocateCommandBuffers"); load_fn(vk_free_command_buffers, "vkFreeCommandBuffers"); load_fn(vk_begin_command_buffer, "vkBeginCommandBuffer"); load_fn(vk_end_command_buffer, "vkEndCommandBuffer"); load_fn(vk_cmd_pipeline_barrier, "vkCmdPipelineBarrier"); load_fn(vk_queue_submit, "vkQueueSubmit"); load_fn(vk_queue_wait_idle, "vkQueueWaitIdle"); load_fn(vk_device_wait_idle, "vkDeviceWaitIdle"); load_fn(vk_create_fence, "vkCreateFence"); load_fn(vk_destroy_fence, "vkDestroyFence"); load_fn(vk_wait_for_fences, "vkWaitForFences"); load_fn(vk_reset_fences, "vkResetFences"); load_fn(vk_create_semaphore, "vkCreateSemaphore"); load_fn(vk_destroy_semaphore, "vkDestroySemaphore"); load_fn(vk_create_swapchain_khr, "vkCreateSwapchainKHR"); load_fn(vk_destroy_swapchain_khr, "vkDestroySwapchainKHR"); load_fn(vk_get_swapchain_images_khr, "vkGetSwapchainImagesKHR"); load_fn(vk_acquire_next_image_khr, "vkAcquireNextImageKHR"); load_fn(vk_queue_present_khr, "vkQueuePresentKHR"); load_fn(vk_create_image_view, "vkCreateImageView"); load_fn(vk_destroy_image_view, "vkDestroyImageView"); load_fn(vk_create_render_pass, "vkCreateRenderPass"); load_fn(vk_destroy_render_pass, "vkDestroyRenderPass"); load_fn(vk_create_frame_buffer, "vkCreateFramebuffer"); load_fn(vk_destroy_frame_buffer, "vkDestroyFramebuffer"); load_fn(vk_create_shader_module, "vkCreateShaderModule"); load_fn(vk_destroy_shader_module, "vkDestroyShaderModule"); load_fn(vk_create_pipeline_layout, "vkCreatePipelineLayout"); load_fn(vk_destroy_pipeline_layout, "vkDestroyPipelineLayout"); load_fn(vk_create_graphics_pipelines, "vkCreateGraphicsPipelines"); load_fn(vk_destroy_pipeline, "vkDestroyPipeline"); load_fn(vk_cmd_begin_render_pass, "vkCmdBeginRenderPass"); load_fn(vk_cmd_end_render_pass, "vkCmdEndRenderPass"); load_fn(vk_cmd_bind_pipeline, "vkCmdBindPipeline"); load_fn(vk_cmd_draw, "vkCmdDraw"); load_fn(vk_cmd_set_viewport, "vkCmdSetViewport"); load_fn(vk_cmd_set_scissors, "vkCmdSetScissor"); load_fn(vk_cmd_push_constants, "vkCmdPushConstants"); load_fn(vk_cmd_copy_buffer, "vkCmdCopyBuffer"); load_fn(vk_create_descriptor_set_layout, "vkCreateDescriptorSetLayout"); load_fn(vk_destroy_descriptor_set_layout, "vkDestroyDescriptorSetLayout"); load_fn(vk_create_descriptor_pool, "vkCreateDescriptorPool"); load_fn(vk_destroy_descriptor_pool, "vkDestroyDescriptorPool"); load_fn(vk_allocate_descriptor_sets, "vkAllocateDescriptorSets"); load_fn(vk_free_descriptor_sets, "vkFreeDescriptorSets"); load_fn(vk_create_buffer, "vkCreateBuffer"); load_fn(vk_destroy_buffer, "vkDestroyBuffer"); load_fn(vk_allocate_memory, "vkAllocateMemory"); load_fn(vk_bind_buffer_memory, "vkBindBufferMemory"); load_fn(vk_map_memory, "vkMapMemory"); load_fn(vk_unmap_memory, "vkUnmapMemory"); load_fn(vk_free_memory, "vkFreeMemory"); load_fn(vk_get_buffer_memory_requirements, "vkGetBufferMemoryRequirements"); load_fn(vk_reset_command_buffer, "vkResetCommandBuffer"); load_fn(vk_cmd_begin_rendering, "vkCmdBeginRendering"); load_fn(vk_cmd_end_rendering, "vkCmdEndRendering"); } Instance::Instance(CreateInfo info) { const auto layer_setting_type_visitor = overloads { [](const std::vector &) { return VK_LAYER_SETTING_TYPE_STRING_EXT; }, [](std::uint32_t) { return VK_LAYER_SETTING_TYPE_UINT32_EXT; }, [](bool) { return VK_LAYER_SETTING_TYPE_BOOL32_EXT; }, }; const auto layer_setting_value_visitor = overloads { [](std::vector values) { return std::bit_cast(values.data()); }, [](std::uint32_t value) { return std::bit_cast(&value); }, [](bool value) { return std::bit_cast(&value); }, }; auto layer_settings = std::vector {}; auto layer_names = std::vector {}; auto extension_names = std::vector {}; for (const auto &layer : info.layers) { layer_names.emplace_back(layer.name.c_str()); for (const auto &setting : layer.settings) { layer_settings.emplace_back( VkLayerSettingEXT { .pLayerName = layer.name.c_str(), .pSettingName = setting.name.c_str(), .type = std::visit(layer_setting_type_visitor, setting.values), .valueCount = 1u, .pValues = std::visit(layer_setting_value_visitor, setting.values), } ); } } const auto layer_settings_create_info = VkLayerSettingsCreateInfoEXT { .sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT, .settingCount = static_cast(layer_settings.size()), .pSettings = layer_settings.data(), }; auto vk_info = VkInstanceCreateInfo { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pNext = &layer_settings_create_info, .flags = {}, .enabledLayerCount = static_cast(info.layers.size()), .ppEnabledLayerNames = layer_names.data(), .enabledExtensionCount = static_cast(info.extensions.size()), .ppEnabledExtensionNames = extension_names.data(), }; vkc(vk_create_instance(&vk_info, nullptr, &m_instance)); debug::ensure(m_instance, "Failed to create vulkan instance"); } Surface::Surface(const Instance &instance, const XlibCreateInfo &info) : m_instance(instance.m_instance) { const auto vk_info = VkXlibSurfaceCreateInfoKHR { .sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, .pNext = {}, .flags = {}, .dpy = info.display, .window = info.window, }; vkc(vk_create_xlib_surface_khr(instance.m_instance, &vk_info, nullptr, &m_surface)); } Surface::~Surface() { vk_destroy_surface_khr(m_instance, m_surface, nullptr); } [[nodiscard]] /* static */ auto Gpu::enumerate(const Instance &instance) -> std::vector { auto count = 0u; vkc(vk_enumerate_physical_devices(instance.m_instance, &count, nullptr)); debug::ensure(count != 0u, "Failed to find any gpus with Vulkan support"); auto vk_gpus = std::vector(count); vkc(vk_enumerate_physical_devices(instance.m_instance, &count, vk_gpus.data())); auto gpus = std::vector(count); for (auto [vk_gpu, gpu] : std::views::zip(vk_gpus, gpus)) { gpu.m_instance = instance.m_instance; gpu.m_physical_device = vk_gpu; } return gpus; } [[nodiscard]] auto Gpu::queue_family_supports_surface( const Surface &surface, std::uint32_t queue_family_idx ) const -> bool { auto supported = VkBool32 { false }; vkc(vk_get_physical_device_surface_support( m_physical_device, queue_family_idx, surface.m_surface, &supported )); return supported; } Device::Device(CreateInfo info) { } Device::~Device() { if (m_device) { vk_destroy_device(m_device, nullptr); } } [[nodiscard]] auto enumerate_instance_extension_properties() -> std::vector { auto count = 0u; vkc(vk_enumerate_instance_extension_properties(nullptr, &count, nullptr)); auto extensions = std::vector(count); std::memset(extensions.data(), 0, extensions.size() * sizeof(VkExtensionProperties)); vkc(vk_enumerate_instance_extension_properties(nullptr, &count, extensions.data())); return extensions; }