commit b7ac53ddca85f8611a79ca6dedcd8756a8ce7d58 Author: Janis Date: Sat Oct 19 00:21:43 2024 +0200 initial commit diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..666c2bd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,24 @@ +[workspace] +resolver = "2" + +members = [ + "crates/renderer", + "crates/window", + "crates/game" + ] + +[workspace.dependencies] +anyhow = "1.0.89" +ash = "0.38.0" +ash-window = "0.13.0" +glam = "0.29.0" +thiserror = "1.0.64" +tracing = "0.1.40" +tracing-subscriber = "0.3.18" +vk-mem = "0.4.0" +vk-sync = "0.1.6" +winit = "0.30.5" + +futures = "0.3" +smol = "2.0" +rayon = "1.10" diff --git a/crates/game/.gitignore b/crates/game/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/crates/game/.gitignore @@ -0,0 +1 @@ +/target diff --git a/crates/game/Cargo.toml b/crates/game/Cargo.toml new file mode 100644 index 0000000..2b7b124 --- /dev/null +++ b/crates/game/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "game" +version = "0.1.0" +edition = "2021" + +[dependencies] +winit = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +renderer = { path = "../renderer" } \ No newline at end of file diff --git a/crates/game/src/main.rs b/crates/game/src/main.rs new file mode 100644 index 0000000..0964d8c --- /dev/null +++ b/crates/game/src/main.rs @@ -0,0 +1,114 @@ +use std::collections::BTreeMap; + +use tracing::info; +use winit::{ + application::ApplicationHandler, + dpi::{LogicalSize, PhysicalSize}, + event_loop::EventLoop, + raw_window_handle::HasDisplayHandle, + window::{Window, WindowAttributes, WindowId}, +}; + +struct WindowState { + last_resize_events: BTreeMap>, + window_attrs: WindowAttributes, + window: Option, +} + +impl WindowState { + fn new(window_title: String) -> WindowState { + Self { + window: None, + last_resize_events: BTreeMap::new(), + window_attrs: WindowAttributes::default() + .with_title(window_title) + .with_resizable(true) + .with_inner_size(LogicalSize::new(800, 600)), + } + } + + fn handle_final_resize(&mut self, window_id: WindowId, new_size: PhysicalSize) { + _ = (window_id, new_size); + info!("TODO: implement resize events"); + } + + fn handle_draw_request(&mut self, window_id: WindowId) { + _ = (window_id); + } +} + +impl ApplicationHandler for WindowState { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + tracing::debug!("winit::resumed"); + self.window = Some(event_loop.create_window(self.window_attrs.clone()).unwrap()); + tracing::info!( + window = u64::from(self.window.as_ref().unwrap().id()), + "created new window" + ); + } + + fn about_to_wait(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) { + for (&window, &resize) in self.last_resize_events.clone().iter() { + self.handle_final_resize(window, resize); + } + self.last_resize_events.clear(); + } + + fn window_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + window_id: winit::window::WindowId, + event: winit::event::WindowEvent, + ) { + if !matches!(event, winit::event::WindowEvent::Resized(_)) { + if let Some(resize) = self.last_resize_events.remove(&window_id) { + self.handle_final_resize(window_id, resize); + } + } + match event { + winit::event::WindowEvent::Resized(physical_size) => { + _ = self.last_resize_events.insert(window_id, physical_size); + } + winit::event::WindowEvent::CloseRequested => { + tracing::info!(window = u64::from(window_id), "window close requested"); + event_loop.exit(); + } + winit::event::WindowEvent::KeyboardInput { + device_id, + event, + is_synthetic, + } => { + _ = (device_id, event, is_synthetic); + } + winit::event::WindowEvent::CursorMoved { + device_id, + position, + } => { + _ = (device_id, position); + } + winit::event::WindowEvent::MouseInput { + device_id, + state, + button, + } => { + _ = (device_id, state, button); + } + winit::event::WindowEvent::RedrawRequested => { + self.handle_draw_request(window_id); + if let Some(window) = self.window.as_ref() { + window.request_redraw(); + } + } + _ => {} // unhandled event + } + } +} + +fn main() { + tracing_subscriber::fmt().init(); + let ev = EventLoop::new().unwrap(); + + let display = ev.owned_display_handle().display_handle(); + let mut game = WindowState::new("Vidya".to_owned()); + ev.run_app(&mut game).unwrap(); +} diff --git a/crates/renderer/.gitignore b/crates/renderer/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/crates/renderer/.gitignore @@ -0,0 +1 @@ +/target diff --git a/crates/renderer/Cargo.toml b/crates/renderer/Cargo.toml new file mode 100644 index 0000000..276ec0a --- /dev/null +++ b/crates/renderer/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "renderer" +version = "0.1.0" +edition = "2021" + +[dependencies] +dyn-clone = "1" +anyhow = "1.0.89" +ash = "0.38.0" +ash-window = "0.13.0" +glam = "0.29.0" +thiserror = "1.0.64" +tracing = "0.1.40" +tracing-subscriber = "0.3.18" +vk-mem = "0.4.0" +vk-sync = "0.1.6" +winit = "0.30.5" diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs new file mode 100644 index 0000000..595f423 --- /dev/null +++ b/crates/renderer/src/lib.rs @@ -0,0 +1,1689 @@ +#![feature(c_str_module, closure_lifetime_binder)] +use std::{ + borrow::Borrow, + collections::{BTreeMap, BTreeSet}, + ffi::CStr, + fmt::Debug, + marker::PhantomData, + ops::Deref, + sync::{Arc, Mutex}, +}; + +use ash::{khr, prelude::*, vk, Entry}; +use dyn_clone::DynClone; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + LoadingError(#[from] ash::LoadingError), + #[error(transparent)] + Result(#[from] ash::vk::Result), + #[error(transparent)] + CStrError(#[from] core::ffi::c_str::FromBytesUntilNulError), + #[error("No Physical Device found.")] + NoPhysicalDevice, +} + +type Result = core::result::Result; + +struct VkNameList<'a> { + names: Vec<*const i8>, + _pd: PhantomData<&'a ()>, +} + +impl<'a> VkNameList<'a> { + fn from_strs(strs: &[&'a CStr]) -> Self { + let names = strs.iter().map(|str| str.as_ptr()).collect::>(); + + Self { + names, + _pd: PhantomData, + } + } +} + +#[derive(Debug, Clone)] +struct DeviceExtension<'a> { + name: &'a core::ffi::CStr, + version: u32, +} + +fn make_extention_properties(name: &CStr, version: u32) -> vk::ExtensionProperties { + vk::ExtensionProperties::default() + .spec_version(version) + .extension_name(name) + .unwrap() +} + +/// returns true if lhs and rhs have the same name and lhs spec_version is less +/// than or equal to rhs spec_version +fn compatible_extension_properties( + lhs: &vk::ExtensionProperties, + rhs: &vk::ExtensionProperties, +) -> bool { + let Some(lhs_name) = lhs.extension_name_as_c_str().ok() else { + return false; + }; + let Some(rhs_name) = rhs.extension_name_as_c_str().ok() else { + return false; + }; + + if lhs_name == rhs_name { + lhs.spec_version <= rhs.spec_version + } else { + false + } +} + +#[derive(Clone, Debug)] +struct Queue(Arc>); + +impl Queue { + fn new(device: &ash::Device, family: u32, index: u32) -> Self { + Self(Arc::new(Mutex::new(unsafe { + device.get_device_queue(family, index) + }))) + } +} + +trait ExtendsDeviceFeatures2Debug: vk::ExtendsPhysicalDeviceFeatures2 + Debug {} +trait ExtendsDeviceProperties2Debug: vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone {} + +impl ExtendsDeviceFeatures2Debug for T {} +impl ExtendsDeviceProperties2Debug + for T +{ +} + +#[derive(Default, Debug)] +struct PhysicalDeviceFeatures { + version: u32, + physical_features_10: vk::PhysicalDeviceFeatures, + physical_features_11: Option>, + physical_features_12: Option>, + physical_features_13: Option>, + extra_features: Vec>, + device_extensions: Vec, +} + +impl PhysicalDeviceFeatures { + fn version(self, version: u32) -> Self { + Self { version, ..self } + } + fn all_default() -> Self { + Self::default() + .features11(Default::default()) + .features12(Default::default()) + .features13(Default::default()) + } + + fn query(instance: &ash::Instance, pdev: vk::PhysicalDevice) -> Result { + let mut this = Self::all_default(); + let mut features2 = this.features2(); + let features = unsafe { + instance.get_physical_device_features2(pdev, &mut features2); + // allocate and query again + features2.features + }; + this = this.features10(features); + + let extensions = unsafe { instance.enumerate_device_extension_properties(pdev)? }; + this = this.device_extensions(extensions); + + Ok(this) + } + + fn features10(self, physical_features_10: vk::PhysicalDeviceFeatures) -> Self { + Self { + physical_features_10, + ..self + } + } + fn features11(self, physical_features_11: vk::PhysicalDeviceVulkan11Features<'static>) -> Self { + Self { + physical_features_11: Some(physical_features_11), + ..self + } + } + fn features12(self, physical_features_12: vk::PhysicalDeviceVulkan12Features<'static>) -> Self { + Self { + physical_features_12: Some(physical_features_12), + ..self + } + } + fn features13(self, physical_features_13: vk::PhysicalDeviceVulkan13Features<'static>) -> Self { + Self { + physical_features_13: Some(physical_features_13), + ..self + } + } + fn device_extensions(self, device_extensions: Vec) -> Self { + Self { + device_extensions, + ..self + } + } + + fn with_extension(mut self, ext: vk::ExtensionProperties, features: F) -> Self + where + F: ExtendsDeviceFeatures2Debug + 'static, + { + self.extra_features.push(Box::new(features)); + self.device_extensions.push(ext); + + self + } + + fn features2(&mut self) -> vk::PhysicalDeviceFeatures2<'_> { + let mut features2 = + vk::PhysicalDeviceFeatures2::default().features(self.physical_features_10); + + if let Some(ref mut features11) = self.physical_features_11 { + features2 = features2.push_next(features11); + } + if let Some(ref mut features12) = self.physical_features_12 { + features2 = features2.push_next(features12); + } + if let Some(ref mut features13) = self.physical_features_13 { + features2 = features2.push_next(features13); + } + + for features in self.extra_features.iter_mut() { + features2 = features2.push_next(Box::as_mut(features)); + } + + features2 + } + + fn compatible_with(&self, device: &Self) -> bool { + let sort_exts = |a: &vk::ExtensionProperties, b: &vk::ExtensionProperties| { + (a.extension_name_as_c_str().unwrap(), a.spec_version) + .cmp(&(b.extension_name_as_c_str().unwrap(), b.spec_version)) + }; + let mut device_extensions = device.device_extensions.clone(); + device_extensions.sort_by(sort_exts); + + let unsupported_extensions = self + .device_extensions + .iter() + .filter(|ext| { + !device_extensions + .binary_search_by(|t| sort_exts(t, ext)) + .is_ok() + }) + .cloned() + .collect::>(); + + let supports_extensions = unsupported_extensions.is_empty(); + + supports_extensions + && utils::eq_device_features10( + &utils::bitand_device_features10( + &self.physical_features_10, + &device.physical_features_10, + ), + &self.physical_features_10, + ) + && self + .physical_features_11 + .zip(device.physical_features_11) + .map(|(a, b)| { + utils::eq_device_features11(&utils::bitand_device_features11(&a, &b), &a) + }) + .unwrap_or(true) + && self + .physical_features_12 + .zip(device.physical_features_12) + .map(|(a, b)| { + utils::eq_device_features12(&utils::bitand_device_features12(&a, &b), &a) + }) + .unwrap_or(true) + && self + .physical_features_13 + .zip(device.physical_features_13) + .map(|(a, b)| { + utils::eq_device_features13(&utils::bitand_device_features13(&a, &b), &a) + }) + .unwrap_or(true) + } +} + +#[derive(Debug, Default)] +struct PhysicalDeviceProperties { + base: vk::PhysicalDeviceProperties, + vk11: vk::PhysicalDeviceVulkan11Properties<'static>, + vk12: vk::PhysicalDeviceVulkan12Properties<'static>, + vk13: vk::PhysicalDeviceVulkan13Properties<'static>, + extra_properties: Vec>, +} + +impl PhysicalDeviceProperties { + fn query(&mut self, instance: &ash::Instance, pdev: vk::PhysicalDevice) { + let mut props2 = self.properties2(); + unsafe { + instance.get_physical_device_properties2(pdev, &mut props2); + self.base = props2.properties; + } + } + + fn extra_properties( + mut self, + extra_properties: Vec>, + ) -> Self { + self.extra_properties = extra_properties; + self + } + + fn with_properties(mut self, properties: F) -> Self + where + F: ExtendsDeviceProperties2Debug + 'static, + { + self.extra_properties.push(Box::new(properties)); + + self + } + + fn properties2(&mut self) -> vk::PhysicalDeviceProperties2<'_> { + let mut props2 = vk::PhysicalDeviceProperties2::default() + .properties(self.base) + .push_next(&mut self.vk11) + .push_next(&mut self.vk12) + .push_next(&mut self.vk13); + + for props in &mut self.extra_properties { + props2 = props2.push_next(Box::as_mut(props)); + } + + props2 + } +} + +#[derive(Debug)] +struct PhysicalDevice { + pdev: vk::PhysicalDevice, + queue_families: DeviceQueueFamilies, + properties: PhysicalDeviceProperties, +} + +struct Instance { + entry: Entry, + instance: ash::Instance, + debug_utils: ash::ext::debug_utils::Instance, + debug_utils_messenger: vk::DebugUtilsMessengerEXT, + surface: ash::khr::surface::Instance, +} + +impl Drop for Instance { + fn drop(&mut self) { + unsafe { + self.debug_utils + .destroy_debug_utils_messenger(self.debug_utils_messenger, None); + } + } +} + +impl AsRef for Instance { + fn as_ref(&self) -> &ash::Instance { + &self.instance + } +} +impl AsRef for Instance { + fn as_ref(&self) -> &ash::khr::surface::Instance { + &self.surface + } +} + +#[derive(Debug, Default)] +struct DeviceQueueFamilies { + graphics: u32, + present: Option, + async_compute: Option, + transfer: Option, +} + +#[derive(Clone)] +struct Device { + device: ash::Device, + swapchain: khr::swapchain::Device, +} + +impl Device { + fn new(device: ash::Device, swapchain: khr::swapchain::Device) -> Self { + Self { device, swapchain } + } +} + +impl AsRef for Device { + fn as_ref(&self) -> &khr::swapchain::Device { + &self.swapchain + } +} + +impl AsRef for Device { + fn as_ref(&self) -> &ash::Device { + &self.device + } +} + +impl Drop for Device { + fn drop(&mut self) { + unsafe { + _ = self.device.device_wait_idle(); + self.device.destroy_device(None); + } + } +} + +struct DeviceAndQueues { + physical: PhysicalDevice, + logical: Arc, + main_queue: Queue, + compute_queue: Queue, + transfer_queue: Queue, + present_queue: Queue, +} + +impl AsRef for DeviceAndQueues { + fn as_ref(&self) -> &ash::Device { + &self.logical.device + } +} +impl AsRef for DeviceAndQueues { + fn as_ref(&self) -> &ash::khr::swapchain::Device { + &self.logical.swapchain + } +} + +struct Swapchain { + device: Arc, + swapchain: vk::SwapchainKHR, + present_mode: vk::PresentModeKHR, + color_space: vk::ColorSpaceKHR, + images: Vec, + image_views: Vec, + extent: vk::Extent2D, +} + +impl Drop for Swapchain { + fn drop(&mut self) { + unsafe { + for view in &self.image_views { + self.device.device.destroy_image_view(*view, None); + } + self.device + .swapchain + .destroy_swapchain(self.swapchain, None); + } + } +} + +impl Swapchain { + fn create_new( + instance: Arc, + device: Arc, + surface: vk::SurfaceKHR, + pdev: vk::PhysicalDevice, + queue_families: &[u32], + extent: vk::Extent2D, + ) -> Result { + let caps = unsafe { + instance + .surface + .get_physical_device_surface_capabilities(pdev, surface)? + }; + let formats = unsafe { + instance + .surface + .get_physical_device_surface_formats(pdev, surface)? + }; + let present_modes = unsafe { + instance + .surface + .get_physical_device_surface_present_modes(pdev, surface)? + }; + + let present_mode = present_modes + .iter() + .find(|&mode| mode == &vk::PresentModeKHR::MAILBOX) + .cloned() + .unwrap_or(vk::PresentModeKHR::FIFO); + + let format = formats + .iter() + .max_by_key(|&&format| { + let is_rgba_unorm = format.format == vk::Format::R8G8B8A8_UNORM + || format.format == vk::Format::B8G8R8A8_UNORM; + let is_srgb = format.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR; + is_rgba_unorm as u8 * 10 + is_srgb as u8 + }) + .or(formats.first()) + .cloned() + .context("no surface format available!")?; + + let image_count = caps.min_image_count.max( + core::num::NonZeroU32::new(surface_caps.max_image_count) + .or(core::num::NonZero::new(u32::MAX)) + .unwrap() + .get() + .min(3u32), + ); + let image_count = 3u32.clamp(caps.min_image_count, caps.max_image_count); + + let extent = if caps.current_extent.width == u32::MAX { + vk::Extent2D { + width: extent + .width + .clamp(caps.min_image_extent.width, caps.max_image_extent.width), + height: extent + .height + .clamp(caps.min_image_extent.height, caps.max_image_extent.height), + } + } else { + caps.current_extent + }; + + todo!() + } +} + +struct Surface { + instance: Arc, + surface: vk::SurfaceKHR, + swapchain: Option, +} + +impl Surface { + fn create( + instance: Arc, + device: Arc, + display_handle: winit::raw_window_handle::RawDisplayHandle, + window_handle: winit::raw_window_handle::RawWindowHandle, + ) -> Result { + let surface = unsafe { + ash_window::create_surface( + &instance.entry, + &instance.instance, + display_handle, + window_handle, + None, + )? + }; + + let mut this = Self { + instance, + surface, + swapchain: None, + }; + + todo!() + } +} + +impl Drop for Surface { + fn drop(&mut self) { + unsafe { + self.instance.surface.destroy_surface(self.surface, None); + } + } +} + +pub struct Vulkan { + instance: Arc, + device: DeviceAndQueues, + alloc: Arc, +} + +impl Vulkan { + const VALIDATION_LAYER_NAME: &'static core::ffi::CStr = c"VK_LAYER_KHRONOS_validation"; + #[allow(unused)] + const RENDERDOC_LAYER_NAME: &'static core::ffi::CStr = c"VK_LAYER_RENDERDOC_Capture"; + #[allow(unused)] + const NSIGHT_TRACE_LAYER_NAME: &'static core::ffi::CStr = + c"VK_LAYER_NV_GPU_Trace_release_public_2021_4_2"; + #[allow(unused)] + const NSIGHT_INTERCEPTION_LAYER_NAME: &'static core::ffi::CStr = + c"VK_LAYER_NV_nomad_release_public_2021_4_2"; + + pub fn new( + instance_layers: &[&CStr], + instance_extensions: &[&CStr], + display_handle: winit::raw_window_handle::DisplayHandle, + ) -> Result { + let entry = unsafe { ash::Entry::load()? }; + + let app_info = vk::ApplicationInfo::default() + .api_version(vk::make_api_version(0, 1, 3, 0)) + .application_name(c"Vidya") + .engine_name(c"Vidya") + .application_version(0) + .engine_version(0); + + // TODO: make this a flag somewhere to enable or disable validation layers + // DEBUG LAYERS/VALIDATION + let validation_settings = [ + vk::LayerSettingEXT::default() + .layer_name(Self::VALIDATION_LAYER_NAME) + .setting_name(c"VK_KHRONOS_VALIDATION_VALIDATE_BEST_PRACTICES") + .ty(vk::LayerSettingTypeEXT::BOOL32) + .values(&[1]), + vk::LayerSettingEXT::default() + .layer_name(Self::VALIDATION_LAYER_NAME) + .setting_name(c"VK_KHRONOS_VALIDATION_VALIDATE_BEST_PRACTICES_AMD") + .ty(vk::LayerSettingTypeEXT::BOOL32) + .values(&[1]), + vk::LayerSettingEXT::default() + .layer_name(Self::VALIDATION_LAYER_NAME) + .setting_name(c"VK_KHRONOS_VALIDATION_VALIDATE_SYNC") + .ty(vk::LayerSettingTypeEXT::BOOL32) + .values(&[1]), + ]; + let mut validation_info = + vk::LayerSettingsCreateInfoEXT::default().settings(&validation_settings); + + let layers = Self::get_layers(&entry, &[Self::VALIDATION_LAYER_NAME]).unwrap(); + let extensions = Self::get_extensions( + &entry, + &layers, + &[ash::ext::debug_utils::NAME, ash::ext::layer_settings::NAME], + display_handle, + ) + .unwrap(); + + let layers = VkNameList::from_strs(&layers); + let extensions = VkNameList::from_strs(&extensions); + + let create_info = vk::InstanceCreateInfo::default() + .application_info(&app_info) + .enabled_extension_names(&extensions.names) + .enabled_layer_names(&layers.names) + .push_next(&mut validation_info); + let instance = unsafe { entry.create_instance(&create_info, None)? }; + + let debug_info = vk::DebugUtilsMessengerCreateInfoEXT::default() + .message_severity( + vk::DebugUtilsMessageSeverityFlagsEXT::ERROR + | vk::DebugUtilsMessageSeverityFlagsEXT::WARNING + | vk::DebugUtilsMessageSeverityFlagsEXT::INFO, + ) + .message_type( + vk::DebugUtilsMessageTypeFlagsEXT::GENERAL + | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION + | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE, + ) + .pfn_user_callback(Some(debug::debug_callback)); + + let debug_utils_instance = ash::ext::debug_utils::Instance::new(&entry, &instance); + let debug_utils_messenger = + unsafe { debug_utils_instance.create_debug_utils_messenger(&debug_info, None)? }; + let surface_instance = ash::khr::surface::Instance::new(&entry, &instance); + + let instance = Arc::new(Instance { + instance, + debug_utils: debug_utils_instance, + debug_utils_messenger, + surface: surface_instance, + entry, + }); + + let mut features = PhysicalDeviceFeatures::all_default() + .version(vk::make_api_version(0, 1, 3, 0)) + .features10( + vk::PhysicalDeviceFeatures::default() + .sampler_anisotropy(true) + .multi_draw_indirect(true), + ) + .features11(vk::PhysicalDeviceVulkan11Features::default().shader_draw_parameters(true)) + .features12( + vk::PhysicalDeviceVulkan12Features::default() + .shader_int8(true) + .storage_buffer8_bit_access(true), + ) + .features13( + vk::PhysicalDeviceVulkan13Features::default() + .dynamic_rendering(true) + .maintenance4(true) + .synchronization2(true), + ) + .with_extension( + make_extention_properties( + ash::ext::mesh_shader::NAME, + ash::ext::mesh_shader::SPEC_VERSION, + ), + vk::PhysicalDeviceMeshShaderFeaturesEXT::default() + .mesh_shader(true) + .task_shader(true), + ) + .with_extension( + make_extention_properties( + ash::khr::index_type_uint8::NAME, + ash::khr::index_type_uint8::SPEC_VERSION, + ), + vk::PhysicalDeviceIndexTypeUint8FeaturesKHR::default().index_type_uint8(true), + ) + .device_extensions(vec![ + make_extention_properties(khr::swapchain::NAME, khr::swapchain::SPEC_VERSION), + make_extention_properties(khr::spirv_1_4::NAME, khr::spirv_1_4::SPEC_VERSION), + ]); + + // Consider this: switching physical device in game? + // anything above this point is device agnostic, everything below would have to be recreated + // additionally, pdev would have to be derived from a device and not a scoring function. + + let pdev = Self::choose_physical_device( + &instance, + display_handle, + &features, + vec![Box::new( + vk::PhysicalDeviceMeshShaderPropertiesEXT::default(), + )], + )?; + + tracing::debug!("pdev: {pdev:?}"); + let dev = Self::create_device(&instance, pdev, &mut features)?; + + let alloc_info = + vk_mem::AllocatorCreateInfo::new(&instance.instance, dev.as_ref(), dev.physical.pdev); + + let alloc = Arc::new(unsafe { vk_mem::Allocator::new(alloc_info)? }); + + Ok(Self { + instance, + device: dev, + alloc, + }) + } + + fn queue_family_supports_presentation( + instance: &Instance, + pdev: vk::PhysicalDevice, + queue_family: u32, + display_handle: winit::raw_window_handle::DisplayHandle, + ) -> bool { + unsafe { + match display_handle.as_raw() { + winit::raw_window_handle::RawDisplayHandle::Xlib(_xlib_display_handle) => { + todo!() + } + winit::raw_window_handle::RawDisplayHandle::Xcb(_xcb_display_handle) => todo!(), + winit::raw_window_handle::RawDisplayHandle::Wayland(wayland_display_handle) => { + ash::khr::wayland_surface::Instance::new(&instance.entry, &instance.instance) + .get_physical_device_wayland_presentation_support( + pdev, + queue_family, + wayland_display_handle.display.cast().as_mut(), + ) + } + winit::raw_window_handle::RawDisplayHandle::Drm(_) => { + todo!() + } + winit::raw_window_handle::RawDisplayHandle::Windows(_) => { + ash::khr::win32_surface::Instance::new(&instance.entry, &instance.instance) + .get_physical_device_win32_presentation_support(pdev, queue_family) + } + _ => panic!("unsupported platform"), + } + } + } + + fn select_pdev_queue_families( + instance: &Instance, + display_handle: winit::raw_window_handle::DisplayHandle, + pdev: vk::PhysicalDevice, + ) -> DeviceQueueFamilies { + let queue_families = unsafe { + instance + .instance + .get_physical_device_queue_family_properties(pdev) + }; + + struct QueueFamily { + num_queues: u32, + is_present: bool, + is_compute: bool, + is_graphics: bool, + is_transfer: bool, + } + + impl QueueFamily { + fn is_graphics_and_compute(&self) -> bool { + self.is_compute && self.is_graphics + } + } + + struct QueueFamilies(Vec); + impl QueueFamilies { + fn find_first(&mut self, mut pred: F) -> Option + where + F: FnMut(&QueueFamily) -> bool, + { + if let Some((q, family)) = self + .0 + .iter_mut() + .enumerate() + .filter(|(_, family)| family.num_queues > 0) + .find(|(_, family)| pred(family)) + { + family.num_queues -= 1; + Some(q as u32) + } else { + None + } + } + + fn find_best(&mut self, mut pred: F) -> Option + where + F: FnMut(&QueueFamily) -> Option, + { + let (_, q, family) = self + .0 + .iter_mut() + .enumerate() + .filter_map(|(i, family)| { + if family.num_queues == 0 { + return None; + } + pred(family).map(|score| (score, i, family)) + }) + .max_by_key(|(score, _, _)| *score)?; + family.num_queues -= 1; + Some(q as u32) + } + } + + let mut queue_families = QueueFamilies( + queue_families + .iter() + .enumerate() + .map(|(i, family)| { + let q = i as u32; + let is_graphics = family.queue_flags.contains(vk::QueueFlags::GRAPHICS); + let is_compute = family.queue_flags.contains(vk::QueueFlags::COMPUTE); + let is_transfer = family.queue_flags.contains(vk::QueueFlags::TRANSFER) + || is_compute + || is_graphics; + let is_present = + Self::queue_family_supports_presentation(instance, pdev, q, display_handle); + QueueFamily { + num_queues: family.queue_count, + is_compute, + is_graphics, + is_present, + is_transfer, + } + }) + .collect::>(), + ); + + let graphics = queue_families + .find_best(|family| { + if !family.is_graphics { + return None; + } + // a queue with Graphics+Compute is guaranteed to exist + Some(family.is_compute as u32 * 2 + family.is_present as u32) + }) + .unwrap(); + + // find present queue first because it is rather more important than a secondary compute queue + let present = if !queue_families.0.get(graphics as usize).unwrap().is_present { + // unwrap because we do need a present queue + Some( + queue_families + .find_first(|family| family.is_present) + .unwrap(), + ) + } else { + None + }; + + let async_compute = queue_families.find_first(|family| family.is_compute); + let transfer = queue_families.find_first(|family| family.is_transfer); + + // family of each queue, of which one is allocated for each queue, with graphics being the fallback queue for compute and transfer, and present possibly being `None`, in which case it is Graphics + let queues = DeviceQueueFamilies { + graphics, + async_compute, + transfer, + present: present.or({ + if !queue_families.0.get(graphics as usize).unwrap().is_present { + panic!("no present queue available"); + } else { + None + } + }), + }; + + queues + } + + fn create_device( + instance: &Instance, + pdev: PhysicalDevice, + features: &mut PhysicalDeviceFeatures, + ) -> Result { + let mut unique_families = BTreeMap::::new(); + + let mut helper = |family: u32| { + use std::collections::btree_map::Entry; + let index = match unique_families.entry(family) { + Entry::Vacant(vacant_entry) => *vacant_entry.insert(0), + Entry::Occupied(mut occupied_entry) => { + let idx = occupied_entry.get_mut(); + *idx += 1; + *idx - 1 + } + }; + + (family, index) + }; + + let graphics_family_and_index = helper(pdev.queue_families.graphics); + let compute_family_and_index = pdev.queue_families.async_compute.map(|f| helper(f)); + let transfer_family_and_index = pdev.queue_families.transfer.map(|f| helper(f)); + let present_family_and_index = pdev.queue_families.present.map(|f| helper(f)); + + let priorities = + vec![1.0f32; unique_families.iter().fold(0, |acc, (_, num)| acc + *num) as usize]; + + let queue_infos = unique_families + .into_iter() + .map(|(family, queues)| { + vk::DeviceQueueCreateInfo::default() + .queue_family_index(family) + .queue_priorities(&priorities[..queues as usize]) + }) + .collect::>(); + + let extensions = features + .device_extensions + .iter() + .map(|ext| ext.extension_name.as_ptr()) + .collect::>(); + + let mut features2 = features.features2(); + let device_info = vk::DeviceCreateInfo::default() + .queue_create_infos(&queue_infos) + .enabled_extension_names(&extensions) + .push_next(&mut features2); + + let device_and_queues = unsafe { + let device = instance + .instance + .create_device(pdev.pdev, &device_info, None)?; + let main_queue = Queue::new( + &device, + graphics_family_and_index.0, + graphics_family_and_index.1, + ); + device.get_device_queue(graphics_family_and_index.0, graphics_family_and_index.1); + let present_queue = present_family_and_index + .map(|(f, i)| Queue::new(&device, f, i)) + .unwrap_or(main_queue.clone()); + let compute_queue = compute_family_and_index + .map(|(f, i)| Queue::new(&device, f, i)) + .unwrap_or(main_queue.clone()); + let transfer_queue = transfer_family_and_index + .map(|(f, i)| Queue::new(&device, f, i)) + .unwrap_or(compute_queue.clone()); + + DeviceAndQueues { + physical: pdev, + logical: Arc::new(Device::new( + device.clone(), + khr::swapchain::Device::new(&instance.instance, &device), + )), + main_queue, + present_queue, + compute_queue, + transfer_queue, + } + }; + + Ok(device_and_queues) + } + + fn choose_physical_device( + instance: &Instance, + display_handle: winit::raw_window_handle::DisplayHandle, + requirements: &PhysicalDeviceFeatures, + extra_properties: Vec>, + ) -> Result { + let pdevs = unsafe { instance.instance.enumerate_physical_devices()? }; + + let (pdev, properties) = pdevs + .into_iter() + .map(|pdev| { + let mut props = PhysicalDeviceProperties::default().extra_properties( + extra_properties + .iter() + .map(|b| dyn_clone::clone_box(&**b)) + .collect::>(), + ); + props.query(&instance.instance, pdev); + + (pdev, props) + }) + // filter devices which dont support the version of Vulkan we are requesting + .filter(|(_, props)| props.base.api_version >= requirements.version) + // filter devices which don't support the device extensions we + // are requesting + // TODO: figure out a way to fall back to some + // device which doesn't support all of the extensions. + .filter(|(pdev, _)| { + let query_features = + PhysicalDeviceFeatures::query(&instance.instance, *pdev).unwrap(); + + requirements.compatible_with(&query_features) + }) + .max_by_key(|(_, props)| { + let score = match props.base.device_type { + vk::PhysicalDeviceType::DISCRETE_GPU => 5, + vk::PhysicalDeviceType::INTEGRATED_GPU => 4, + vk::PhysicalDeviceType::VIRTUAL_GPU => 3, + vk::PhysicalDeviceType::CPU => 2, + vk::PhysicalDeviceType::OTHER => 1, + _ => unreachable!(), + }; + + // score based on limits or other properties + + score + }) + .ok_or(Error::NoPhysicalDevice)?; + + Ok(PhysicalDevice { + queue_families: Self::select_pdev_queue_families(instance, display_handle, pdev), + pdev, + properties, + }) + } + + fn get_available_extensions( + entry: &ash::Entry, + layers: &[&CStr], + ) -> Result> { + unsafe { + let extensions = core::iter::once(entry.enumerate_instance_extension_properties(None)) + .chain( + layers + .iter() + .map(|&layer| entry.enumerate_instance_extension_properties(Some(layer))), + ) + .filter_map(|result| result.ok()) + .flatten() + .collect::>(); + + Ok(extensions) + } + } + + fn get_extensions<'a>( + entry: &ash::Entry, + layers: &[&'a CStr], + extensions: &[&'a CStr], + display_handle: winit::raw_window_handle::DisplayHandle, + ) -> core::result::Result, (Vec<&'a CStr>, Vec<&'a CStr>)> { + unsafe { + let available_extensions = Self::get_available_extensions(entry, layers) + .map_err(|_| (Vec::<&'a CStr>::new(), extensions.to_vec()))?; + + let available_extension_names = available_extensions + .iter() + .map(|layer| layer.extension_name_as_c_str()) + .collect::, _>>() + .map_err(|_| (Vec::<&'a CStr>::new(), extensions.to_vec()))?; + + let mut out_extensions = Vec::with_capacity(extensions.len()); + let mut unsupported_extensions = Vec::with_capacity(extensions.len()); + for &extension in extensions { + if available_extension_names.contains(&extension) { + out_extensions.push(extension); + } else { + unsupported_extensions.push(extension); + } + } + + let Ok(required_extension_names) = + ash_window::enumerate_required_extensions(display_handle.as_raw()) + else { + return Err((out_extensions, unsupported_extensions)); + }; + + for &extension in required_extension_names { + let extension = core::ffi::CStr::from_ptr(extension); + if available_extension_names.contains(&extension) { + out_extensions.push(extension); + } else { + unsupported_extensions.push(extension); + } + } + + if !unsupported_extensions.is_empty() { + Err((out_extensions, unsupported_extensions)) + } else { + Ok(out_extensions) + } + } + } + + fn get_layers<'a>( + entry: &ash::Entry, + wants_layers: &[&'a CStr], + ) -> core::result::Result, (Vec<&'a CStr>, Vec<&'a CStr>)> { + unsafe { + let available_layers = entry + .enumerate_instance_layer_properties() + .map_err(|_| (Vec::<&'a CStr>::new(), wants_layers.to_vec()))?; + let available_layer_names = available_layers + .iter() + .map(|layer| layer.layer_name_as_c_str()) + .collect::, _>>() + .map_err(|_| (Vec::<&'a CStr>::new(), wants_layers.to_vec()))?; + + let mut out_layers = Vec::with_capacity(wants_layers.len()); + let mut unsupported_layers = Vec::with_capacity(wants_layers.len()); + for &layer in wants_layers { + if available_layer_names.contains(&layer) { + out_layers.push(layer); + } else { + unsupported_layers.push(layer); + } + } + + if !unsupported_layers.is_empty() { + Err((out_layers, unsupported_layers)) + } else { + Ok(out_layers) + } + } + } +} + +pub struct Renderer {} + +mod debug { + use ash::vk; + use tracing::{event, Level}; + + unsafe fn str_from_raw_parts<'a>(str: *const i8) -> std::borrow::Cow<'a, str> { + use std::{borrow::Cow, ffi}; + if str.is_null() { + Cow::from("") + } else { + ffi::CStr::from_ptr(str).to_string_lossy() + } + } + + pub(super) unsafe extern "system" fn debug_callback( + message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, + message_type: vk::DebugUtilsMessageTypeFlagsEXT, + callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT<'_>, + user_data: *mut core::ffi::c_void, + ) -> vk::Bool32 { + _ = user_data; + let callback_data = *callback_data; + let message_id_number = callback_data.message_id_number; + + let message_id_name = str_from_raw_parts(callback_data.p_message_id_name); + let message = str_from_raw_parts(callback_data.p_message); + + match message_severity { + vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => { + event!(target: "VK::DebugUtils", Level::ERROR, "{message_type:?} [{message_id_name}({message_id_number})]: {message}"); + } + vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => { + event!(target: "VK::DebugUtils", Level::TRACE, "{message_type:?} [{message_id_name}({message_id_number})]: {message}"); + } + vk::DebugUtilsMessageSeverityFlagsEXT::INFO => { + event!(target: "VK::DebugUtils", Level::INFO, "{message_type:?} [{message_id_name}({message_id_number})]: {message}"); + } + vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => { + event!(target: "VK::DebugUtils", Level::WARN, "{message_type:?} [{message_id_name}({message_id_number})]: {message}"); + } + _ => unreachable!(), + } + + vk::FALSE + } +} + +pub mod utils { + #![allow(dead_code)] + + use ash::vk; + pub trait SplitCollect: Iterator { + fn collect2(self, mut pred: F) -> (Vec, Vec) + where + Self: Sized, + F: FnMut(&Self::Item) -> bool, + { + let mut left = Vec::new(); + let mut right = Vec::new(); + + for item in self { + if pred(&item) { + left.push(item); + } else { + right.push(item); + } + } + + (left, right) + } + } + + pub fn eq_device_features10( + lhs: &vk::PhysicalDeviceFeatures, + rhs: &vk::PhysicalDeviceFeatures, + ) -> bool { + lhs.robust_buffer_access == rhs.robust_buffer_access + && lhs.full_draw_index_uint32 == rhs.full_draw_index_uint32 + && lhs.image_cube_array == rhs.image_cube_array + && lhs.independent_blend == rhs.independent_blend + && lhs.geometry_shader == rhs.geometry_shader + && lhs.tessellation_shader == rhs.tessellation_shader + && lhs.sample_rate_shading == rhs.sample_rate_shading + && lhs.dual_src_blend == rhs.dual_src_blend + && lhs.logic_op == rhs.logic_op + && lhs.multi_draw_indirect == rhs.multi_draw_indirect + && lhs.draw_indirect_first_instance == rhs.draw_indirect_first_instance + && lhs.depth_clamp == rhs.depth_clamp + && lhs.depth_bias_clamp == rhs.depth_bias_clamp + && lhs.fill_mode_non_solid == rhs.fill_mode_non_solid + && lhs.depth_bounds == rhs.depth_bounds + && lhs.wide_lines == rhs.wide_lines + && lhs.large_points == rhs.large_points + && lhs.alpha_to_one == rhs.alpha_to_one + && lhs.multi_viewport == rhs.multi_viewport + && lhs.sampler_anisotropy == rhs.sampler_anisotropy + && lhs.texture_compression_etc2 == rhs.texture_compression_etc2 + && lhs.texture_compression_astc_ldr == rhs.texture_compression_astc_ldr + && lhs.texture_compression_bc == rhs.texture_compression_bc + && lhs.occlusion_query_precise == rhs.occlusion_query_precise + && lhs.pipeline_statistics_query == rhs.pipeline_statistics_query + && lhs.vertex_pipeline_stores_and_atomics == rhs.vertex_pipeline_stores_and_atomics + && lhs.fragment_stores_and_atomics == rhs.fragment_stores_and_atomics + && lhs.shader_tessellation_and_geometry_point_size + == rhs.shader_tessellation_and_geometry_point_size + && lhs.shader_image_gather_extended == rhs.shader_image_gather_extended + && lhs.shader_storage_image_extended_formats + == rhs.shader_storage_image_extended_formats + && lhs.shader_storage_image_multisample == rhs.shader_storage_image_multisample + && lhs.shader_storage_image_read_without_format + == rhs.shader_storage_image_read_without_format + && lhs.shader_storage_image_write_without_format + == rhs.shader_storage_image_write_without_format + && lhs.shader_uniform_buffer_array_dynamic_indexing + == rhs.shader_uniform_buffer_array_dynamic_indexing + && lhs.shader_sampled_image_array_dynamic_indexing + == rhs.shader_sampled_image_array_dynamic_indexing + && lhs.shader_storage_buffer_array_dynamic_indexing + == rhs.shader_storage_buffer_array_dynamic_indexing + && lhs.shader_storage_image_array_dynamic_indexing + == rhs.shader_storage_image_array_dynamic_indexing + && lhs.shader_clip_distance == rhs.shader_clip_distance + && lhs.shader_cull_distance == rhs.shader_cull_distance + && lhs.shader_float64 == rhs.shader_float64 + && lhs.shader_int64 == rhs.shader_int64 + && lhs.shader_int16 == rhs.shader_int16 + && lhs.shader_resource_residency == rhs.shader_resource_residency + && lhs.shader_resource_min_lod == rhs.shader_resource_min_lod + && lhs.sparse_binding == rhs.sparse_binding + && lhs.sparse_residency_buffer == rhs.sparse_residency_buffer + && lhs.sparse_residency_image2_d == rhs.sparse_residency_image2_d + && lhs.sparse_residency_image3_d == rhs.sparse_residency_image3_d + && lhs.sparse_residency2_samples == rhs.sparse_residency2_samples + && lhs.sparse_residency4_samples == rhs.sparse_residency4_samples + && lhs.sparse_residency8_samples == rhs.sparse_residency8_samples + && lhs.sparse_residency16_samples == rhs.sparse_residency16_samples + && lhs.sparse_residency_aliased == rhs.sparse_residency_aliased + && lhs.variable_multisample_rate == rhs.variable_multisample_rate + && lhs.inherited_queries == rhs.inherited_queries + } + + pub fn eq_device_features11( + lhs: &vk::PhysicalDeviceVulkan11Features, + rhs: &vk::PhysicalDeviceVulkan11Features, + ) -> bool { + lhs.storage_buffer16_bit_access == rhs.storage_buffer16_bit_access + && lhs.uniform_and_storage_buffer16_bit_access + == rhs.uniform_and_storage_buffer16_bit_access + && lhs.storage_push_constant16 == rhs.storage_push_constant16 + && lhs.storage_input_output16 == rhs.storage_input_output16 + && lhs.multiview == rhs.multiview + && lhs.multiview_geometry_shader == rhs.multiview_geometry_shader + && lhs.multiview_tessellation_shader == rhs.multiview_tessellation_shader + && lhs.variable_pointers_storage_buffer == rhs.variable_pointers_storage_buffer + && lhs.variable_pointers == rhs.variable_pointers + && lhs.protected_memory == rhs.protected_memory + && lhs.sampler_ycbcr_conversion == rhs.sampler_ycbcr_conversion + && lhs.shader_draw_parameters == rhs.shader_draw_parameters + } + + pub fn eq_device_features12( + lhs: &vk::PhysicalDeviceVulkan12Features, + rhs: &vk::PhysicalDeviceVulkan12Features, + ) -> bool { + lhs.sampler_mirror_clamp_to_edge == rhs.sampler_mirror_clamp_to_edge + && lhs.draw_indirect_count == rhs.draw_indirect_count + && lhs.storage_buffer8_bit_access == rhs.storage_buffer8_bit_access + && lhs.uniform_and_storage_buffer8_bit_access + == rhs.uniform_and_storage_buffer8_bit_access + && lhs.storage_push_constant8 == rhs.storage_push_constant8 + && lhs.shader_buffer_int64_atomics == rhs.shader_buffer_int64_atomics + && lhs.shader_shared_int64_atomics == rhs.shader_shared_int64_atomics + && lhs.shader_float16 == rhs.shader_float16 + && lhs.shader_int8 == rhs.shader_int8 + && lhs.descriptor_indexing == rhs.descriptor_indexing + && lhs.shader_input_attachment_array_dynamic_indexing + == rhs.shader_input_attachment_array_dynamic_indexing + && lhs.shader_uniform_texel_buffer_array_dynamic_indexing + == rhs.shader_uniform_texel_buffer_array_dynamic_indexing + && lhs.shader_storage_texel_buffer_array_dynamic_indexing + == rhs.shader_storage_texel_buffer_array_dynamic_indexing + && lhs.shader_uniform_buffer_array_non_uniform_indexing + == rhs.shader_uniform_buffer_array_non_uniform_indexing + && lhs.shader_sampled_image_array_non_uniform_indexing + == rhs.shader_sampled_image_array_non_uniform_indexing + && lhs.shader_storage_buffer_array_non_uniform_indexing + == rhs.shader_storage_buffer_array_non_uniform_indexing + && lhs.shader_storage_image_array_non_uniform_indexing + == rhs.shader_storage_image_array_non_uniform_indexing + && lhs.shader_input_attachment_array_non_uniform_indexing + == rhs.shader_input_attachment_array_non_uniform_indexing + && lhs.shader_uniform_texel_buffer_array_non_uniform_indexing + == rhs.shader_uniform_texel_buffer_array_non_uniform_indexing + && lhs.shader_storage_texel_buffer_array_non_uniform_indexing + == rhs.shader_storage_texel_buffer_array_non_uniform_indexing + && lhs.descriptor_binding_uniform_buffer_update_after_bind + == rhs.descriptor_binding_uniform_buffer_update_after_bind + && lhs.descriptor_binding_sampled_image_update_after_bind + == rhs.descriptor_binding_sampled_image_update_after_bind + && lhs.descriptor_binding_storage_image_update_after_bind + == rhs.descriptor_binding_storage_image_update_after_bind + && lhs.descriptor_binding_storage_buffer_update_after_bind + == rhs.descriptor_binding_storage_buffer_update_after_bind + && lhs.descriptor_binding_uniform_texel_buffer_update_after_bind + == rhs.descriptor_binding_uniform_texel_buffer_update_after_bind + && lhs.descriptor_binding_storage_texel_buffer_update_after_bind + == rhs.descriptor_binding_storage_texel_buffer_update_after_bind + && lhs.descriptor_binding_update_unused_while_pending + == rhs.descriptor_binding_update_unused_while_pending + && lhs.descriptor_binding_partially_bound == rhs.descriptor_binding_partially_bound + && lhs.descriptor_binding_variable_descriptor_count + == rhs.descriptor_binding_variable_descriptor_count + && lhs.runtime_descriptor_array == rhs.runtime_descriptor_array + && lhs.sampler_filter_minmax == rhs.sampler_filter_minmax + && lhs.scalar_block_layout == rhs.scalar_block_layout + && lhs.imageless_framebuffer == rhs.imageless_framebuffer + && lhs.uniform_buffer_standard_layout == rhs.uniform_buffer_standard_layout + && lhs.shader_subgroup_extended_types == rhs.shader_subgroup_extended_types + && lhs.separate_depth_stencil_layouts == rhs.separate_depth_stencil_layouts + && lhs.host_query_reset == rhs.host_query_reset + && lhs.timeline_semaphore == rhs.timeline_semaphore + && lhs.buffer_device_address == rhs.buffer_device_address + && lhs.buffer_device_address_capture_replay == rhs.buffer_device_address_capture_replay + && lhs.buffer_device_address_multi_device == rhs.buffer_device_address_multi_device + && lhs.vulkan_memory_model == rhs.vulkan_memory_model + && lhs.vulkan_memory_model_device_scope == rhs.vulkan_memory_model_device_scope + && lhs.vulkan_memory_model_availability_visibility_chains + == rhs.vulkan_memory_model_availability_visibility_chains + && lhs.shader_output_viewport_index == rhs.shader_output_viewport_index + && lhs.shader_output_layer == rhs.shader_output_layer + && lhs.subgroup_broadcast_dynamic_id == rhs.subgroup_broadcast_dynamic_id + } + + pub fn eq_device_features13( + lhs: &vk::PhysicalDeviceVulkan13Features, + rhs: &vk::PhysicalDeviceVulkan13Features, + ) -> bool { + lhs.robust_image_access == rhs.robust_image_access + && lhs.inline_uniform_block == rhs.inline_uniform_block + && lhs.descriptor_binding_inline_uniform_block_update_after_bind + == rhs.descriptor_binding_inline_uniform_block_update_after_bind + && lhs.pipeline_creation_cache_control == rhs.pipeline_creation_cache_control + && lhs.private_data == rhs.private_data + && lhs.shader_demote_to_helper_invocation == rhs.shader_demote_to_helper_invocation + && lhs.shader_terminate_invocation == rhs.shader_terminate_invocation + && lhs.subgroup_size_control == rhs.subgroup_size_control + && lhs.compute_full_subgroups == rhs.compute_full_subgroups + && lhs.synchronization2 == rhs.synchronization2 + && lhs.texture_compression_astc_hdr == rhs.texture_compression_astc_hdr + && lhs.shader_zero_initialize_workgroup_memory + == rhs.shader_zero_initialize_workgroup_memory + && lhs.dynamic_rendering == rhs.dynamic_rendering + && lhs.shader_integer_dot_product == rhs.shader_integer_dot_product + && lhs.maintenance4 == rhs.maintenance4 + } + + pub fn bitand_device_features10( + lhs: &vk::PhysicalDeviceFeatures, + rhs: &vk::PhysicalDeviceFeatures, + ) -> vk::PhysicalDeviceFeatures { + use core::ops::BitAnd; + vk::PhysicalDeviceFeatures { + robust_buffer_access: lhs.robust_buffer_access.bitand(&rhs.robust_buffer_access), + full_draw_index_uint32: lhs + .full_draw_index_uint32 + .bitand(&rhs.full_draw_index_uint32), + image_cube_array: lhs.image_cube_array.bitand(&rhs.image_cube_array), + independent_blend: lhs.independent_blend.bitand(&rhs.independent_blend), + geometry_shader: lhs.geometry_shader.bitand(&rhs.geometry_shader), + tessellation_shader: lhs.tessellation_shader.bitand(&rhs.tessellation_shader), + sample_rate_shading: lhs.sample_rate_shading.bitand(&rhs.sample_rate_shading), + dual_src_blend: lhs.dual_src_blend.bitand(&rhs.dual_src_blend), + logic_op: lhs.logic_op.bitand(&rhs.logic_op), + multi_draw_indirect: lhs.multi_draw_indirect.bitand(&rhs.multi_draw_indirect), + draw_indirect_first_instance: lhs + .draw_indirect_first_instance + .bitand(&rhs.draw_indirect_first_instance), + depth_clamp: lhs.depth_clamp.bitand(&rhs.depth_clamp), + depth_bias_clamp: lhs.depth_bias_clamp.bitand(&rhs.depth_bias_clamp), + fill_mode_non_solid: lhs.fill_mode_non_solid.bitand(&rhs.fill_mode_non_solid), + depth_bounds: lhs.depth_bounds.bitand(&rhs.depth_bounds), + wide_lines: lhs.wide_lines.bitand(&rhs.wide_lines), + large_points: lhs.large_points.bitand(&rhs.large_points), + alpha_to_one: lhs.alpha_to_one.bitand(&rhs.alpha_to_one), + multi_viewport: lhs.multi_viewport.bitand(&rhs.multi_viewport), + sampler_anisotropy: lhs.sampler_anisotropy.bitand(&rhs.sampler_anisotropy), + texture_compression_etc2: lhs + .texture_compression_etc2 + .bitand(&rhs.texture_compression_etc2), + texture_compression_astc_ldr: lhs + .texture_compression_astc_ldr + .bitand(&rhs.texture_compression_astc_ldr), + texture_compression_bc: lhs + .texture_compression_bc + .bitand(&rhs.texture_compression_bc), + occlusion_query_precise: lhs + .occlusion_query_precise + .bitand(&rhs.occlusion_query_precise), + pipeline_statistics_query: lhs + .pipeline_statistics_query + .bitand(&rhs.pipeline_statistics_query), + vertex_pipeline_stores_and_atomics: lhs + .vertex_pipeline_stores_and_atomics + .bitand(&rhs.vertex_pipeline_stores_and_atomics), + fragment_stores_and_atomics: lhs + .fragment_stores_and_atomics + .bitand(&rhs.fragment_stores_and_atomics), + shader_tessellation_and_geometry_point_size: lhs + .shader_tessellation_and_geometry_point_size + .bitand(&rhs.shader_tessellation_and_geometry_point_size), + shader_image_gather_extended: lhs + .shader_image_gather_extended + .bitand(&rhs.shader_image_gather_extended), + shader_storage_image_extended_formats: lhs + .shader_storage_image_extended_formats + .bitand(&rhs.shader_storage_image_extended_formats), + shader_storage_image_multisample: lhs + .shader_storage_image_multisample + .bitand(&rhs.shader_storage_image_multisample), + shader_storage_image_read_without_format: lhs + .shader_storage_image_read_without_format + .bitand(&rhs.shader_storage_image_read_without_format), + shader_storage_image_write_without_format: lhs + .shader_storage_image_write_without_format + .bitand(&rhs.shader_storage_image_write_without_format), + shader_uniform_buffer_array_dynamic_indexing: lhs + .shader_uniform_buffer_array_dynamic_indexing + .bitand(&rhs.shader_uniform_buffer_array_dynamic_indexing), + shader_sampled_image_array_dynamic_indexing: lhs + .shader_sampled_image_array_dynamic_indexing + .bitand(&rhs.shader_sampled_image_array_dynamic_indexing), + shader_storage_buffer_array_dynamic_indexing: lhs + .shader_storage_buffer_array_dynamic_indexing + .bitand(&rhs.shader_storage_buffer_array_dynamic_indexing), + shader_storage_image_array_dynamic_indexing: lhs + .shader_storage_image_array_dynamic_indexing + .bitand(&rhs.shader_storage_image_array_dynamic_indexing), + shader_clip_distance: lhs.shader_clip_distance.bitand(&rhs.shader_clip_distance), + shader_cull_distance: lhs.shader_cull_distance.bitand(&rhs.shader_cull_distance), + shader_float64: lhs.shader_float64.bitand(&rhs.shader_float64), + shader_int64: lhs.shader_int64.bitand(&rhs.shader_int64), + shader_int16: lhs.shader_int16.bitand(&rhs.shader_int16), + shader_resource_residency: lhs + .shader_resource_residency + .bitand(&rhs.shader_resource_residency), + shader_resource_min_lod: lhs + .shader_resource_min_lod + .bitand(&rhs.shader_resource_min_lod), + sparse_binding: lhs.sparse_binding.bitand(&rhs.sparse_binding), + sparse_residency_buffer: lhs + .sparse_residency_buffer + .bitand(&rhs.sparse_residency_buffer), + sparse_residency_image2_d: lhs + .sparse_residency_image2_d + .bitand(&rhs.sparse_residency_image2_d), + sparse_residency_image3_d: lhs + .sparse_residency_image3_d + .bitand(&rhs.sparse_residency_image3_d), + sparse_residency2_samples: lhs + .sparse_residency2_samples + .bitand(&rhs.sparse_residency2_samples), + sparse_residency4_samples: lhs + .sparse_residency4_samples + .bitand(&rhs.sparse_residency4_samples), + sparse_residency8_samples: lhs + .sparse_residency8_samples + .bitand(&rhs.sparse_residency8_samples), + sparse_residency16_samples: lhs + .sparse_residency16_samples + .bitand(&rhs.sparse_residency16_samples), + sparse_residency_aliased: lhs + .sparse_residency_aliased + .bitand(&rhs.sparse_residency_aliased), + variable_multisample_rate: lhs + .variable_multisample_rate + .bitand(&rhs.variable_multisample_rate), + inherited_queries: lhs.inherited_queries.bitand(&rhs.inherited_queries), + } + } + + pub fn bitand_device_features11( + lhs: &vk::PhysicalDeviceVulkan11Features, + rhs: &vk::PhysicalDeviceVulkan11Features, + ) -> vk::PhysicalDeviceVulkan11Features<'static> { + use core::ops::BitAnd; + vk::PhysicalDeviceVulkan11Features { + storage_buffer16_bit_access: lhs + .storage_buffer16_bit_access + .bitand(&rhs.storage_buffer16_bit_access), + uniform_and_storage_buffer16_bit_access: lhs + .uniform_and_storage_buffer16_bit_access + .bitand(&rhs.uniform_and_storage_buffer16_bit_access), + storage_push_constant16: lhs + .storage_push_constant16 + .bitand(&rhs.storage_push_constant16), + storage_input_output16: lhs + .storage_input_output16 + .bitand(&rhs.storage_input_output16), + multiview: lhs.multiview.bitand(&rhs.multiview), + multiview_geometry_shader: lhs + .multiview_geometry_shader + .bitand(&rhs.multiview_geometry_shader), + multiview_tessellation_shader: lhs + .multiview_tessellation_shader + .bitand(&rhs.multiview_tessellation_shader), + variable_pointers_storage_buffer: lhs + .variable_pointers_storage_buffer + .bitand(&rhs.variable_pointers_storage_buffer), + variable_pointers: lhs.variable_pointers.bitand(&rhs.variable_pointers), + protected_memory: lhs.protected_memory.bitand(&rhs.protected_memory), + sampler_ycbcr_conversion: lhs + .sampler_ycbcr_conversion + .bitand(&rhs.sampler_ycbcr_conversion), + shader_draw_parameters: lhs + .shader_draw_parameters + .bitand(&rhs.shader_draw_parameters), + ..Default::default() + } + } + + pub fn bitand_device_features12( + lhs: &vk::PhysicalDeviceVulkan12Features, + rhs: &vk::PhysicalDeviceVulkan12Features, + ) -> vk::PhysicalDeviceVulkan12Features<'static> { + use core::ops::BitAnd; + vk::PhysicalDeviceVulkan12Features { + sampler_mirror_clamp_to_edge: lhs + .sampler_mirror_clamp_to_edge + .bitand(&rhs.sampler_mirror_clamp_to_edge), + draw_indirect_count: lhs.draw_indirect_count.bitand(&rhs.draw_indirect_count), + storage_buffer8_bit_access: lhs + .storage_buffer8_bit_access + .bitand(&rhs.storage_buffer8_bit_access), + uniform_and_storage_buffer8_bit_access: lhs + .uniform_and_storage_buffer8_bit_access + .bitand(&rhs.uniform_and_storage_buffer8_bit_access), + storage_push_constant8: lhs + .storage_push_constant8 + .bitand(&rhs.storage_push_constant8), + shader_buffer_int64_atomics: lhs + .shader_buffer_int64_atomics + .bitand(&rhs.shader_buffer_int64_atomics), + shader_shared_int64_atomics: lhs + .shader_shared_int64_atomics + .bitand(&rhs.shader_shared_int64_atomics), + shader_float16: lhs.shader_float16.bitand(&rhs.shader_float16), + shader_int8: lhs.shader_int8.bitand(&rhs.shader_int8), + descriptor_indexing: lhs.descriptor_indexing.bitand(&rhs.descriptor_indexing), + shader_input_attachment_array_dynamic_indexing: lhs + .shader_input_attachment_array_dynamic_indexing + .bitand(&rhs.shader_input_attachment_array_dynamic_indexing), + shader_uniform_texel_buffer_array_dynamic_indexing: lhs + .shader_uniform_texel_buffer_array_dynamic_indexing + .bitand(&rhs.shader_uniform_texel_buffer_array_dynamic_indexing), + shader_storage_texel_buffer_array_dynamic_indexing: lhs + .shader_storage_texel_buffer_array_dynamic_indexing + .bitand(&rhs.shader_storage_texel_buffer_array_dynamic_indexing), + shader_uniform_buffer_array_non_uniform_indexing: lhs + .shader_uniform_buffer_array_non_uniform_indexing + .bitand(&rhs.shader_uniform_buffer_array_non_uniform_indexing), + shader_sampled_image_array_non_uniform_indexing: lhs + .shader_sampled_image_array_non_uniform_indexing + .bitand(&rhs.shader_sampled_image_array_non_uniform_indexing), + shader_storage_buffer_array_non_uniform_indexing: lhs + .shader_storage_buffer_array_non_uniform_indexing + .bitand(&rhs.shader_storage_buffer_array_non_uniform_indexing), + shader_storage_image_array_non_uniform_indexing: lhs + .shader_storage_image_array_non_uniform_indexing + .bitand(&rhs.shader_storage_image_array_non_uniform_indexing), + shader_input_attachment_array_non_uniform_indexing: lhs + .shader_input_attachment_array_non_uniform_indexing + .bitand(&rhs.shader_input_attachment_array_non_uniform_indexing), + shader_uniform_texel_buffer_array_non_uniform_indexing: lhs + .shader_uniform_texel_buffer_array_non_uniform_indexing + .bitand(&rhs.shader_uniform_texel_buffer_array_non_uniform_indexing), + shader_storage_texel_buffer_array_non_uniform_indexing: lhs + .shader_storage_texel_buffer_array_non_uniform_indexing + .bitand(&rhs.shader_storage_texel_buffer_array_non_uniform_indexing), + descriptor_binding_uniform_buffer_update_after_bind: lhs + .descriptor_binding_uniform_buffer_update_after_bind + .bitand(&rhs.descriptor_binding_uniform_buffer_update_after_bind), + descriptor_binding_sampled_image_update_after_bind: lhs + .descriptor_binding_sampled_image_update_after_bind + .bitand(&rhs.descriptor_binding_sampled_image_update_after_bind), + descriptor_binding_storage_image_update_after_bind: lhs + .descriptor_binding_storage_image_update_after_bind + .bitand(&rhs.descriptor_binding_storage_image_update_after_bind), + descriptor_binding_storage_buffer_update_after_bind: lhs + .descriptor_binding_storage_buffer_update_after_bind + .bitand(&rhs.descriptor_binding_storage_buffer_update_after_bind), + descriptor_binding_uniform_texel_buffer_update_after_bind: lhs + .descriptor_binding_uniform_texel_buffer_update_after_bind + .bitand(&rhs.descriptor_binding_uniform_texel_buffer_update_after_bind), + descriptor_binding_storage_texel_buffer_update_after_bind: lhs + .descriptor_binding_storage_texel_buffer_update_after_bind + .bitand(&rhs.descriptor_binding_storage_texel_buffer_update_after_bind), + descriptor_binding_update_unused_while_pending: lhs + .descriptor_binding_update_unused_while_pending + .bitand(&rhs.descriptor_binding_update_unused_while_pending), + descriptor_binding_partially_bound: lhs + .descriptor_binding_partially_bound + .bitand(&rhs.descriptor_binding_partially_bound), + descriptor_binding_variable_descriptor_count: lhs + .descriptor_binding_variable_descriptor_count + .bitand(&rhs.descriptor_binding_variable_descriptor_count), + runtime_descriptor_array: lhs + .runtime_descriptor_array + .bitand(&rhs.runtime_descriptor_array), + sampler_filter_minmax: lhs.sampler_filter_minmax.bitand(&rhs.sampler_filter_minmax), + scalar_block_layout: lhs.scalar_block_layout.bitand(&rhs.scalar_block_layout), + imageless_framebuffer: lhs.imageless_framebuffer.bitand(&rhs.imageless_framebuffer), + uniform_buffer_standard_layout: lhs + .uniform_buffer_standard_layout + .bitand(&rhs.uniform_buffer_standard_layout), + shader_subgroup_extended_types: lhs + .shader_subgroup_extended_types + .bitand(&rhs.shader_subgroup_extended_types), + separate_depth_stencil_layouts: lhs + .separate_depth_stencil_layouts + .bitand(&rhs.separate_depth_stencil_layouts), + host_query_reset: lhs.host_query_reset.bitand(&rhs.host_query_reset), + timeline_semaphore: lhs.timeline_semaphore.bitand(&rhs.timeline_semaphore), + buffer_device_address: lhs.buffer_device_address.bitand(&rhs.buffer_device_address), + buffer_device_address_capture_replay: lhs + .buffer_device_address_capture_replay + .bitand(&rhs.buffer_device_address_capture_replay), + buffer_device_address_multi_device: lhs + .buffer_device_address_multi_device + .bitand(&rhs.buffer_device_address_multi_device), + vulkan_memory_model: lhs.vulkan_memory_model.bitand(&rhs.vulkan_memory_model), + vulkan_memory_model_device_scope: lhs + .vulkan_memory_model_device_scope + .bitand(&rhs.vulkan_memory_model_device_scope), + vulkan_memory_model_availability_visibility_chains: lhs + .vulkan_memory_model_availability_visibility_chains + .bitand(&rhs.vulkan_memory_model_availability_visibility_chains), + shader_output_viewport_index: lhs + .shader_output_viewport_index + .bitand(&rhs.shader_output_viewport_index), + shader_output_layer: lhs.shader_output_layer.bitand(&rhs.shader_output_layer), + subgroup_broadcast_dynamic_id: lhs + .subgroup_broadcast_dynamic_id + .bitand(&rhs.subgroup_broadcast_dynamic_id), + ..Default::default() + } + } + pub fn bitand_device_features13( + lhs: &vk::PhysicalDeviceVulkan13Features, + rhs: &vk::PhysicalDeviceVulkan13Features, + ) -> vk::PhysicalDeviceVulkan13Features<'static> { + use core::ops::BitAnd; + vk::PhysicalDeviceVulkan13Features { + robust_image_access: lhs.robust_image_access.bitand(&rhs.robust_image_access), + inline_uniform_block: lhs.inline_uniform_block.bitand(&rhs.inline_uniform_block), + descriptor_binding_inline_uniform_block_update_after_bind: lhs + .descriptor_binding_inline_uniform_block_update_after_bind + .bitand(&rhs.descriptor_binding_inline_uniform_block_update_after_bind), + pipeline_creation_cache_control: lhs + .pipeline_creation_cache_control + .bitand(&rhs.pipeline_creation_cache_control), + private_data: lhs.private_data.bitand(&rhs.private_data), + shader_demote_to_helper_invocation: lhs + .shader_demote_to_helper_invocation + .bitand(&rhs.shader_demote_to_helper_invocation), + shader_terminate_invocation: lhs + .shader_terminate_invocation + .bitand(&rhs.shader_terminate_invocation), + subgroup_size_control: lhs.subgroup_size_control.bitand(&rhs.subgroup_size_control), + compute_full_subgroups: lhs + .compute_full_subgroups + .bitand(&rhs.compute_full_subgroups), + synchronization2: lhs.synchronization2.bitand(&rhs.synchronization2), + texture_compression_astc_hdr: lhs + .texture_compression_astc_hdr + .bitand(&rhs.texture_compression_astc_hdr), + shader_zero_initialize_workgroup_memory: lhs + .shader_zero_initialize_workgroup_memory + .bitand(&rhs.shader_zero_initialize_workgroup_memory), + dynamic_rendering: lhs.dynamic_rendering.bitand(&rhs.dynamic_rendering), + shader_integer_dot_product: lhs + .shader_integer_dot_product + .bitand(&rhs.shader_integer_dot_product), + maintenance4: lhs.maintenance4.bitand(&rhs.maintenance4), + ..Default::default() + } + } +} diff --git a/crates/window/.gitignore b/crates/window/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/crates/window/.gitignore @@ -0,0 +1 @@ +/target diff --git a/crates/window/Cargo.toml b/crates/window/Cargo.toml new file mode 100644 index 0000000..3d18dda --- /dev/null +++ b/crates/window/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "window" +version = "0.1.0" +edition = "2021" + +[dependencies] +winit = { workspace = true } \ No newline at end of file diff --git a/crates/window/src/lib.rs b/crates/window/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/crates/window/src/lib.rs @@ -0,0 +1 @@ + diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..bf867e0 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly