diff --git a/Cargo.toml b/Cargo.toml index 5960992..d228731 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,19 +3,22 @@ name = "rosella-rs" version = "0.1.0" edition = "2018" +[features] +__internal_doc_test = [] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ash = "0.34.0" -ash-window = "0.8.0" +ash = "0.35.1+1.2.203.0" +ash-window = "0.9.0" concurrent-queue = "1.2.2" -gpu-allocator = "0.12.0" +gpu-allocator = "0.16.0" +lazy_static = "1.4.0" log = "0.4.14" -topological-sort = "0.1.0" shaderc = "0.7.3" +topological-sort = "0.1.0" nalgebra = "0.29.0" paste = "1.0.6" -winit = "0.25.0" +winit = "0.26.1" xxhash-rust = { version="0.8.2", features=["xxh3", "const_xxh3"] } [dev-dependencies] diff --git a/tests/old_main.rs b/examples/old_main.rs similarity index 54% rename from tests/old_main.rs rename to examples/old_main.rs index feb7511..a5d8493 100644 --- a/tests/old_main.rs +++ b/examples/old_main.rs @@ -1,13 +1,16 @@ -mod test_common; - extern crate ash_window; extern crate winit; +use ash::vk; use winit::event::{Event, WindowEvent}; use winit::event_loop::ControlFlow; use rosella_rs::init::initialization_registry::InitializationRegistry; -use rosella_rs::init::rosella_features::{register_rosella_debug, register_rosella_headless}; +use rosella_rs::init::rosella_features::{register_rosella_debug, register_rosella_headless, register_rosella_present}; +use rosella_rs::objects::Format; +use rosella_rs::objects::image::ImageViewDescription; +use rosella_rs::objects::swapchain::{SwapchainCreateDesc, SwapchainImageSpec}; +use rosella_rs::objects::swapchain_object_set::SwapchainObjectSetBuilder; use rosella_rs::rosella::Rosella; use rosella_rs::window::RosellaWindow; use rosella_rs::shader::{GraphicsContext, GraphicsShader}; @@ -18,6 +21,7 @@ fn setup_rosella(window: &RosellaWindow) -> Rosella { let mut registry = InitializationRegistry::new(); register_rosella_headless(&mut registry); + register_rosella_present(&mut registry); register_rosella_debug(&mut registry, false); match Rosella::new(registry, window, "new_new_rosella_example_scene_1") { @@ -29,22 +33,46 @@ fn setup_rosella(window: &RosellaWindow) -> Rosella { fn main() { env_logger::init(); - let window = RosellaWindow::new("New New Rosella in Rust tm", 1396.0, 752.0); + let window = RosellaWindow::new("New New Rosella in Rust tm", 1000.0, 700.0); let rosella = setup_rosella(&window); + window.handle.set_visible(true); // Application Setup usually goes here. Anything in the window loop is either for closing or for looping. let basic_vertex_format = VertexFormatBuilder::new() .element(data_type::FLOAT, 3) .build(); - GraphicsShader::new(rosella.device.clone(), include_str!("test_resources/triangle.vert").to_string(), include_str!("test_resources/triangle.frag").to_string(), GraphicsContext { + GraphicsShader::new(rosella.device.clone(), include_str!("resources/triangle.vert").to_string(), include_str!("resources/triangle.frag").to_string(), GraphicsContext { mutable_uniforms: Default::default(), push_uniforms: Default::default(), vertex_format: basic_vertex_format, }); println!("Successfully created shaders."); - /*window.event_loop.run(move |event, _, control_flow| { + let capabilities = rosella.device.get_surface_capabilities(rosella.surface).unwrap(); + println!("Capabilities: {:?}", capabilities.get_capabilities()); + + let surface_format = capabilities.get_surface_formats().get(0).unwrap(); + + let desc = SwapchainCreateDesc::make( + SwapchainImageSpec::make( + Format::format_for(surface_format.format), + surface_format.color_space, + 1000, + 700, + ), + capabilities.get_capabilities().min_image_count, + vk::ImageUsageFlags::COLOR_ATTACHMENT, + *capabilities.get_present_modes().get(0).unwrap() + ); + + let mut swapchain_set_builder = SwapchainObjectSetBuilder::new(rosella.device.clone(), rosella.surface, desc, None).unwrap(); + + swapchain_set_builder.add_views(ImageViewDescription::make_full(vk::ImageViewType::TYPE_2D, Format::format_for(surface_format.format), vk::ImageAspectFlags::COLOR)); + + let swapchain_set = swapchain_set_builder.build(); + + window.event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; match event { @@ -60,5 +88,7 @@ fn main() { } _ => (), } - });*/ + }); + + drop(swapchain_set); } diff --git a/tests/test_resources/triangle.frag b/examples/resources/triangle.frag similarity index 100% rename from tests/test_resources/triangle.frag rename to examples/resources/triangle.frag diff --git a/tests/test_resources/triangle.vert b/examples/resources/triangle.vert similarity index 100% rename from tests/test_resources/triangle.vert rename to examples/resources/triangle.vert diff --git a/src/device.rs b/src/device.rs index 62da990..90f4540 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,23 +1,35 @@ +use std::cmp::Ordering; +use std::collections::HashMap; +use std::fmt::{Debug, Formatter, Pointer}; +use std::mem::ManuallyDrop; use std::sync::Arc; use ash::vk; use crate::init::EnabledFeatures; use crate::instance::InstanceContext; +use crate::objects::id::SurfaceId; +use crate::objects::surface::{Surface, SurfaceCapabilities}; use crate::util::extensions::{AsRefOption, ExtensionFunctionSet, VkExtensionInfo, VkExtensionFunctions}; -use crate::UUID; +use crate::{NamedUUID, UUID}; +use crate::objects::allocator::Allocator; -pub struct DeviceContextImpl { +struct DeviceContextImpl { + id: NamedUUID, instance: InstanceContext, device: ash::Device, physical_device: vk::PhysicalDevice, extensions: ExtensionFunctionSet, + allocator: ManuallyDrop, // We need manually drop to ensure it is dropped before the device features: EnabledFeatures, + surfaces: HashMap, } impl Drop for DeviceContextImpl { fn drop(&mut self) { unsafe { + ManuallyDrop::drop(&mut self.allocator); + self.device.destroy_device(None); } } @@ -27,16 +39,29 @@ impl Drop for DeviceContextImpl { pub struct DeviceContext(Arc); impl DeviceContext { - pub fn new(instance: InstanceContext, device: ash::Device, physical_device: vk::PhysicalDevice, extensions: ExtensionFunctionSet, features: EnabledFeatures) -> Self { + pub fn new(instance: InstanceContext, device: ash::Device, physical_device: vk::PhysicalDevice, extensions: ExtensionFunctionSet, features: EnabledFeatures, surfaces: &[Surface]) -> Self { + let surfaces : HashMap<_, _> = surfaces.iter().map(|surface| { + (surface.get_id(), (surface.clone(), SurfaceCapabilities::new(&instance, physical_device, surface.get_handle()).unwrap())) + }).collect(); + + let allocator = Allocator::new(instance.vk().clone(), device.clone(), physical_device); + Self(Arc::new(DeviceContextImpl{ + id: NamedUUID::with_str("Device"), instance, device, physical_device, extensions, + allocator: ManuallyDrop::new(allocator), features, + surfaces, })) } + pub fn get_uuid(&self) -> &NamedUUID { + &self.0.id + } + pub fn get_entry(&self) -> &ash::Entry { self.0.instance.get_entry() } @@ -61,7 +86,46 @@ impl DeviceContext { self.0.extensions.contains(uuid) } + pub fn get_allocator(&self) -> &Allocator { + &self.0.allocator + } + pub fn get_enabled_features(&self) -> &EnabledFeatures { &self.0.features } + + pub fn get_surface(&self, id: SurfaceId) -> Option { + self.0.surfaces.get(&id).map(|data| data.0.clone()) + } + + pub fn get_surface_capabilities(&self, id: SurfaceId) -> Option<&SurfaceCapabilities> { + self.0.surfaces.get(&id).map(|(_, cap)| cap) + } +} + +impl PartialEq for DeviceContext { + fn eq(&self, other: &Self) -> bool { + self.0.id.eq(&other.0.id) + } +} + +impl Eq for DeviceContext { +} + +impl PartialOrd for DeviceContext { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.id.partial_cmp(&other.0.id) + } +} + +impl Ord for DeviceContext { + fn cmp(&self, other: &Self) -> Ordering { + self.0.id.cmp(&other.0.id) + } } + +impl Debug for DeviceContext { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} \ No newline at end of file diff --git a/src/init/application_feature.rs b/src/init/application_feature.rs index a673c5d..1dd3f2a 100644 --- a/src/init/application_feature.rs +++ b/src/init/application_feature.rs @@ -33,7 +33,7 @@ pub trait ApplicationInstanceFeature : FeatureBase { fn enable(&mut self, features: &mut dyn FeatureAccess, info: &instance::InstanceInfo, config: &mut instance::InstanceConfigurator); /// Performs any necessary post creation steps and generates the data that is sent back to the application - fn finish(&mut self, _: &ash::Instance, _: &ExtensionFunctionSet) -> Option> { + fn finish(&mut self, _: &ash::Instance, _: &ExtensionFunctionSet) -> Option> { None } } @@ -59,7 +59,7 @@ pub trait ApplicationDeviceFeature: Send + FeatureBase { /// Configures the device fn enable(&mut self, features: &mut dyn FeatureAccess, info: &device::DeviceInfo, config: &mut device::DeviceConfigurator); - fn finish(&mut self, _: &InstanceContext, _: &ash::Device, _: &ExtensionFunctionSet) -> Option> { + fn finish(&mut self, _: &InstanceContext, _: &ash::Device, _: &ExtensionFunctionSet) -> Option> { None } } diff --git a/src/init/device.rs b/src/init/device.rs index 2883afc..004fa4a 100644 --- a/src/init/device.rs +++ b/src/init/device.rs @@ -41,6 +41,7 @@ use crate::init::initialization_registry::InitializationRegistry; use crate::init::utils::{ExtensionProperties, Feature, FeatureProcessor}; use crate::{NamedUUID, UUID}; use crate::init::EnabledFeatures; +use crate::objects::surface::{Surface, SurfaceCapabilities}; use crate::util::extensions::{DeviceExtensionLoader, DeviceExtensionLoaderFn, ExtensionFunctionSet, VkExtensionInfo}; use crate::rosella::{DeviceContext, InstanceContext, VulkanVersion}; @@ -93,6 +94,7 @@ impl VulkanQueue { pub enum DeviceCreateError { VulkanError(vk::Result), RequiredFeatureNotSupported(NamedUUID), + SurfaceNotSupported, Utf8Error(std::str::Utf8Error), NulError(std::ffi::NulError), ExtensionNotSupported, @@ -122,8 +124,7 @@ impl From for DeviceCreateError { /// This function will consume the device features stored in the registry. /// /// All discovered physical devices will be processed and the most suitable device will be selected. -/// (TODO not implemented yet) -pub fn create_device(registry: &mut InitializationRegistry, instance: InstanceContext) -> Result { +pub fn create_device(registry: &mut InitializationRegistry, instance: InstanceContext, surfaces: &[Surface]) -> Result { let (graph, features) : (Vec<_>, Vec<_>) = registry.take_device_features().into_iter().map( |(name, dependencies, feature, required)| { ((name.clone(), dependencies), (name, feature, required)) @@ -149,7 +150,7 @@ pub fn create_device(registry: &mut InitializationRegistry, instance: InstanceCo (name.clone(), feature.make_instance(), *required) }).collect(); - DeviceBuilder::new(instance.clone(), device, ordering.clone().into_boxed_slice(), feature_instances) + DeviceBuilder::new(instance.clone(), device, ordering.clone().into_boxed_slice(), feature_instances, surfaces) }).collect(); let mut devices : Vec<_> = devices.into_iter().filter_map(|mut device| { @@ -166,7 +167,9 @@ pub fn create_device(registry: &mut InitializationRegistry, instance: InstanceCo return Err(DeviceCreateError::NoSuitableDeviceFound); } - let device = devices.remove(0).build()?; + devices.sort_by(|a, b| b.get_enabled_feature_count().cmp(&a.get_enabled_feature_count())); // Need to reverse ordering to have highest first + + let device = devices.remove(0).build(surfaces)?; Ok(device) } @@ -221,13 +224,15 @@ struct DeviceBuilder { physical_device: vk::PhysicalDevice, info: Option, config: Option, + enabled_features: u32, + surfaces_supported: bool, } impl DeviceBuilder { /// Generates a new builder for some feature set and physical device. /// /// No vulkan functions will be called here. - fn new(instance: InstanceContext, physical_device: vk::PhysicalDevice, order: Box<[NamedUUID]>, features: Vec<(NamedUUID, Box, bool)>) -> Self { + fn new(instance: InstanceContext, physical_device: vk::PhysicalDevice, order: Box<[NamedUUID]>, features: Vec<(NamedUUID, Box, bool)>, surfaces: &[Surface]) -> Self { let processor = FeatureProcessor::new(features.into_iter().map( |(name, feature, required)| (name.get_uuid(), @@ -239,12 +244,16 @@ impl DeviceBuilder { }) ), order); + let surfaces_supported = surfaces.iter().map(|surface| SurfaceCapabilities::new(&instance, physical_device, surface.get_handle()).is_some()).all(|v| v); + Self { processor, instance, physical_device, info: None, config: None, + enabled_features: 0, + surfaces_supported } } @@ -261,6 +270,14 @@ impl DeviceBuilder { self.info = Some(DeviceInfo::new(self.instance.clone(), self.physical_device)?); let info = self.info.as_ref().unwrap(); + let device_name = unsafe { std::ffi::CStr::from_ptr(info.properties_1_0.device_name.as_ptr()).to_str()? }; + log::info!("Found vulkan device \"{}\"({:#8X}) {:?}", device_name, info.properties_1_0.device_id ,info.properties_1_0.device_type); + + if !self.surfaces_supported { + return Err(DeviceCreateError::SurfaceNotSupported) + } + + let mut enabled_features = 0; self.processor.run_pass::( DeviceFeatureState::Initialized, |feature, access| { @@ -271,6 +288,7 @@ impl DeviceBuilder { InitResult::Ok => { log::debug!("Initialized feature {:?}", feature.name); feature.state = DeviceFeatureState::Initialized; + enabled_features += 1; } InitResult::Disable => { feature.state = DeviceFeatureState::Disabled; @@ -285,6 +303,8 @@ impl DeviceBuilder { } )?; + self.enabled_features = enabled_features; + Ok(()) } @@ -321,19 +341,26 @@ impl DeviceBuilder { } /// Creates the vulkan device - fn build(self) -> Result { + fn build(self, surfaces: &[Surface]) -> Result { let instance = self.instance; let info = self.info.expect("Called build but info is none"); let (device, function_set) = self.config.expect("Called build but config is none") .build_device(&info)?; + let device_name = unsafe { std::ffi::CStr::from_ptr(info.properties_1_0.device_name.as_ptr()).to_str()? }; + log::info!("Creating vulkan device \"{}\"({:#8X}) {:?}", device_name, info.properties_1_0.device_id ,info.properties_1_0.device_type); + let features = EnabledFeatures::new(self.processor.into_iter().filter_map( |mut info| { Some((info.name.get_uuid(), info.feature.as_mut().finish(&instance, &device, &function_set))) })); - Ok(DeviceContext::new(instance, device, self.physical_device, function_set, features)) + Ok(DeviceContext::new(instance, device, self.physical_device, function_set, features, surfaces)) + } + + fn get_enabled_feature_count(&self) -> u32 { + self.enabled_features } } diff --git a/src/init/instance.rs b/src/init/instance.rs index 2ef068c..94e5bb8 100644 --- a/src/init/instance.rs +++ b/src/init/instance.rs @@ -30,7 +30,7 @@ use crate::init::application_feature::{ApplicationInstanceFeature, InitResult}; use crate::init::initialization_registry::{InitializationRegistry}; use crate::init::utils::{ExtensionProperties, Feature, FeatureProcessor, LayerProperties}; -use ash::vk; +use ash::{LoadingError, vk}; use ash::vk::{DebugUtilsMessageSeverityFlagsEXT, DebugUtilsMessageTypeFlagsEXT}; use crate::init::EnabledFeatures; use crate::util::extensions::{ExtensionFunctionSet, InstanceExtensionLoader, InstanceExtensionLoaderFn, VkExtensionInfo}; @@ -40,6 +40,7 @@ use crate::rosella::{InstanceContext, VulkanVersion}; #[derive(Debug)] pub enum InstanceCreateError { VulkanError(vk::Result), + LoadingError(ash::LoadingError), Utf8Error(std::str::Utf8Error), NulError(std::ffi::NulError), RequiredFeatureNotSupported(NamedUUID), @@ -53,6 +54,12 @@ impl From for InstanceCreateError { } } +impl From for InstanceCreateError { + fn from(err: LoadingError) -> Self { + InstanceCreateError::LoadingError(err) + } +} + impl From for InstanceCreateError { fn from(err: std::str::Utf8Error) -> Self { InstanceCreateError::Utf8Error(err) @@ -179,7 +186,7 @@ impl InstanceBuilder { if self.info.is_some() { panic!("Called run init pass but info is already some"); } - self.info = Some(InstanceInfo::new(ash::Entry::new() )?); + self.info = Some(InstanceInfo::new(unsafe { ash::Entry::load() }? )?); let info = self.info.as_ref().unwrap(); self.processor.run_pass::( diff --git a/src/init/rosella_features.rs b/src/init/rosella_features.rs index 4253af2..92148fd 100644 --- a/src/init/rosella_features.rs +++ b/src/init/rosella_features.rs @@ -16,13 +16,18 @@ use crate::rosella::VulkanVersion; /// Registers all instance and device features required for rosella to work in headless mode pub fn register_rosella_headless(registry: &mut InitializationRegistry) { KHRGetPhysicalDeviceProperties2::register_into(registry, false); - KHRTimelineSemaphoreInstance::register_into(registry, false); RosellaInstanceBase::register_into(registry, true); - KHRTimelineSemaphoreDevice::register_into(registry, false); + KHRTimelineSemaphore::register_into(registry, false); RosellaDeviceBase::register_into(registry, true); } +/// Registers all instance and device features required for rosella to present images +pub fn register_rosella_present(registry: &mut InitializationRegistry) { + KHRSurface::register_into(registry, true); + KHRSwapchain::register_into(registry, true); +} + /// Registers instance and device features that provide debugging capabilities pub fn register_rosella_debug(registry: &mut InitializationRegistry, required: bool) { RosellaDebug::register_into(registry, required); @@ -34,7 +39,7 @@ pub fn register_rosella_debug(registry: &mut InitializationRegistry, required: b macro_rules! const_instance_feature{ ($struct_name:ty, $name:literal, [$($dependency:expr),*]) => { impl $struct_name { - const NAME: NamedUUID = NamedUUID::new_const($name); + const NAME: NamedUUID = NamedUUID::from_str($name); const DEPENDENCIES: &'static [NamedUUID] = &[$($dependency,)*]; fn register_into(registry: &mut InitializationRegistry, required: bool) { @@ -76,7 +81,7 @@ macro_rules! const_device_feature{ } impl $struct_name { - const NAME: NamedUUID = NamedUUID::new_const($name); + const NAME: NamedUUID = NamedUUID::from_str($name); const DEPENDENCIES: &'static [NamedUUID] = &[$($dependency,)*]; fn register_into(registry: &mut InitializationRegistry, required: bool) { @@ -104,15 +109,10 @@ macro_rules! const_device_feature{ /// Instance feature which provides all requirements needed for rosella to function in headless #[derive(Default)] pub struct RosellaInstanceBase; -const_instance_feature!(RosellaInstanceBase, "rosella:instance_base", [KHRTimelineSemaphoreInstance::NAME]); +const_instance_feature!(RosellaInstanceBase, "rosella:instance_base", []); impl ApplicationInstanceFeature for RosellaInstanceBase { - fn init(&mut self, features: &mut dyn FeatureAccess, _: &InstanceInfo) -> InitResult { - if !features.is_supported(&KHRTimelineSemaphoreInstance::NAME.get_uuid()) { - log::warn!("KHRTimelineSemaphore is not supported"); - return InitResult::Disable; - } - + fn init(&mut self, _: &mut dyn FeatureAccess, _: &InstanceInfo) -> InitResult { InitResult::Ok } @@ -207,42 +207,31 @@ impl ApplicationInstanceFeature for KHRGetPhysicalDeviceProperties2 { } } -/// Instance feature representing the VK_KHR_timeline_semaphore feature set. -/// If the instance version is below 1.2 it will load the extension. +/// Instance feature representing the VK_KHR_surface extension. #[derive(Default)] -pub struct KHRTimelineSemaphoreInstance; -const_instance_feature!(KHRTimelineSemaphoreInstance, "rosella:instance_khr_timeline_semaphore", [KHRGetPhysicalDeviceProperties2::NAME]); +pub struct KHRSurface; +const_instance_feature!(KHRSurface, "rosella:instance_khr_surface", []); -impl ApplicationInstanceFeature for KHRTimelineSemaphoreInstance { - fn init(&mut self, features: &mut dyn FeatureAccess, info: &InstanceInfo) -> InitResult { - if !features.is_supported(&KHRGetPhysicalDeviceProperties2::NAME.get_uuid()) { - log::warn!("KHRGetPhysicalDeviceProperties2 is not supported"); +impl ApplicationInstanceFeature for KHRSurface { + fn init(&mut self, _: &mut dyn FeatureAccess, info: &InstanceInfo) -> InitResult { + if !info.is_extension_supported::() { return InitResult::Disable; } - let core_present = info.get_vulkan_version().is_supported(VulkanVersion::VK_1_2); - if !core_present { - if !info.is_extension_supported::() { - return InitResult::Disable; - } - } - - InitResult::Ok + return InitResult::Ok; } - fn enable(&mut self, _: &mut dyn FeatureAccess, info: &InstanceInfo, config: &mut InstanceConfigurator) { - if !info.get_vulkan_version().is_supported(VulkanVersion::VK_1_2) { - config.enable_extension_no_load::(); - } + fn enable(&mut self, _: &mut dyn FeatureAccess, _: &InstanceInfo, config: &mut InstanceConfigurator) { + config.enable_extension::(); } } /// Device feature representing the VK_KHR_timeline_semaphore feature set. #[derive(Default)] -pub struct KHRTimelineSemaphoreDevice; -const_device_feature!(KHRTimelineSemaphoreDevice, "rosella:device_khr_timeline_semaphore", []); +pub struct KHRTimelineSemaphore; +const_device_feature!(KHRTimelineSemaphore, "rosella:device_khr_timeline_semaphore", []); -impl ApplicationDeviceFeature for KHRTimelineSemaphoreDevice { +impl ApplicationDeviceFeature for KHRTimelineSemaphore { fn init(&mut self, _: &mut dyn FeatureAccess, info: &DeviceInfo) -> InitResult { if info.get_instance().get_version().is_supported(VulkanVersion::VK_1_2) { if info.get_device_1_2_features().unwrap().timeline_semaphore == vk::TRUE { @@ -285,7 +274,7 @@ impl WindowSurface { let extensions = ash_window::enumerate_required_extensions(window).unwrap(); Self { - name: NamedUUID::new_const("rosella:instance_window_surface"), + name: NamedUUID::from_str("rosella:instance_window_surface"), extensions: extensions.into_iter().map(|str| std::ffi::CString::from(str)).collect() } } @@ -329,12 +318,12 @@ impl ApplicationInstanceFeature for WindowSurface { /// Device feature which provides all requirements needed for rosella to function in headless #[derive(Default)] -struct RosellaDeviceBase; -const_device_feature!(RosellaDeviceBase, "rosella:device_base", [KHRTimelineSemaphoreDevice::NAME]); +pub struct RosellaDeviceBase; +const_device_feature!(RosellaDeviceBase, "rosella:device_base", [KHRTimelineSemaphore::NAME]); impl ApplicationDeviceFeature for RosellaDeviceBase { fn init(&mut self, features: &mut dyn FeatureAccess, _: &DeviceInfo) -> InitResult { - if !features.is_supported(&KHRTimelineSemaphoreDevice::NAME.get_uuid()) { + if !features.is_supported(&KHRTimelineSemaphore::NAME.get_uuid()) { return InitResult::Disable; } @@ -344,4 +333,23 @@ impl ApplicationDeviceFeature for RosellaDeviceBase { fn enable(&mut self, _: &mut dyn FeatureAccess, _: &DeviceInfo, config: &mut DeviceConfigurator) { config.add_queue_request(0); // TODO This is just to prevent validation errors } -} \ No newline at end of file +} + +/// Device feature representing the VK_KHR_swapchain extension. +#[derive(Default)] +pub struct KHRSwapchain; +const_device_feature!(KHRSwapchain, "rosella:device_khr_swapchain", []); + +impl ApplicationDeviceFeature for KHRSwapchain { + fn init(&mut self, _: &mut dyn FeatureAccess, info: &DeviceInfo) -> InitResult { + if !info.is_extension_supported::() { + return InitResult::Disable; + } + + InitResult::Ok + } + + fn enable(&mut self, _: &mut dyn FeatureAccess, _: &DeviceInfo, config: &mut DeviceConfigurator) { + config.enable_extension::() + } +} diff --git a/src/init/utils.rs b/src/init/utils.rs index 25dbf1e..93c75f7 100644 --- a/src/init/utils.rs +++ b/src/init/utils.rs @@ -91,7 +91,7 @@ impl ExtensionProperties { } struct EnabledFeature { - data: Option> + data: Option> } pub struct EnabledFeatures { @@ -99,7 +99,7 @@ pub struct EnabledFeatures { } impl EnabledFeatures { - pub(super) fn new>)>>(data: T) -> Self { + pub(super) fn new>)>>(data: T) -> Self { Self{ features: data.map(|(id, data)| (id, EnabledFeature{ data })).collect() } } @@ -110,7 +110,7 @@ impl EnabledFeatures { /// Returns the data associated with some enabled feature. /// If either the feature is not enabled or it did not create any data None is returned. - pub fn get_feature_data(&self, id: &UUID) -> Option<&dyn Any> { + pub fn get_feature_data(&self, id: &UUID) -> Option<&(dyn Any + Send + Sync)> { match self.features.get(id) { None => None, Some(f) => { diff --git a/src/instance.rs b/src/instance.rs index e10ae1a..8bb1968 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1,10 +1,12 @@ +use std::cmp::Ordering; +use std::fmt::{Debug, Formatter}; use std::sync::Arc; use ash::vk; use crate::init::EnabledFeatures; use crate::util::extensions::{AsRefOption, ExtensionFunctionSet, VkExtensionInfo, VkExtensionFunctions}; -use crate::UUID; +use crate::{NamedUUID, UUID}; #[derive(Copy, Clone, Debug)] pub struct VulkanVersion(u32); @@ -28,6 +30,7 @@ impl VulkanVersion { } struct InstanceContextImpl { + id: NamedUUID, version: VulkanVersion, entry: ash::Entry, instance: ash::Instance, @@ -49,6 +52,7 @@ pub struct InstanceContext(Arc); impl InstanceContext { pub fn new(version: VulkanVersion, entry: ash::Entry, instance: ash::Instance, extensions: ExtensionFunctionSet, features: EnabledFeatures) -> Self { Self(Arc::new(InstanceContextImpl{ + id: NamedUUID::with_str("Instance"), version, entry, instance, @@ -57,6 +61,10 @@ impl InstanceContext { })) } + pub fn get_uuid(&self) -> &NamedUUID { + &self.0.id + } + pub fn get_entry(&self) -> &ash::Entry { &self.0.entry } @@ -80,4 +88,31 @@ impl InstanceContext { pub fn get_enabled_features(&self) -> &EnabledFeatures { &self.0.features } +} + +impl PartialEq for InstanceContext { + fn eq(&self, other: &Self) -> bool { + self.0.id.eq(&other.0.id) + } +} + +impl Eq for InstanceContext { +} + +impl PartialOrd for InstanceContext { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.id.partial_cmp(&other.0.id) + } +} + +impl Ord for InstanceContext { + fn cmp(&self, other: &Self) -> Ordering { + self.0.id.cmp(&other.0.id) + } +} + +impl Debug for InstanceContext { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.0.id.fmt(f) + } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 0a48157..5351fdd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,5 +11,5 @@ mod device; pub use util::id::UUID; pub use util::id::NamedUUID; -#[cfg(test)] +#[cfg(any(test, feature = "__internal_doc_test"))] pub use util::test; \ No newline at end of file diff --git a/src/objects/manager/allocator.rs b/src/objects/allocator.rs similarity index 78% rename from src/objects/manager/allocator.rs rename to src/objects/allocator.rs index abe82e9..705cd44 100644 --- a/src/objects/manager/allocator.rs +++ b/src/objects/allocator.rs @@ -1,12 +1,9 @@ -use std::mem::ManuallyDrop; use std::sync::Mutex; use ash::vk; use gpu_allocator::MemoryLocation; use gpu_allocator::vulkan::{AllocationCreateDesc, AllocatorCreateDesc}; -use crate::device::DeviceContext; - #[derive(Debug)] pub enum AllocationError { GpuAllocator(gpu_allocator::AllocationError), @@ -29,26 +26,24 @@ pub enum AllocationStrategy { /// Manages memory allocation for vulkan object /// /// Currently just uses the [`gpu_allocator::vulkan::Allocator`] struct. -pub(super) struct Allocator { - device: DeviceContext, - - // We need to ensure the allocator is dropped before the instance and device are - allocator: ManuallyDrop> +pub struct Allocator { + device: ash::Device, + allocator: Mutex } impl Allocator { - pub fn new(device: DeviceContext) -> Self { + pub fn new(instance: ash::Instance, device: ash::Device, physical_device: vk::PhysicalDevice) -> Self { let allocator = gpu_allocator::vulkan::Allocator::new(&AllocatorCreateDesc{ - instance: device.get_instance().vk().clone(), - device: device.vk().clone(), - physical_device: device.get_physical_device().clone(), + instance, + device: device.clone(), + physical_device, debug_settings: Default::default(), buffer_device_address: false }).unwrap(); Self { device, - allocator: ManuallyDrop::new(Mutex::new(allocator)), + allocator: Mutex::new(allocator), } } @@ -59,7 +54,7 @@ impl Allocator { }; let requirements = unsafe { - self.device.vk().get_buffer_memory_requirements(buffer) + self.device.get_buffer_memory_requirements(buffer) }; let alloc_desc = AllocationCreateDesc{ @@ -81,7 +76,7 @@ impl Allocator { }; let requirements = unsafe { - self.device.vk().get_image_memory_requirements(image) + self.device.get_image_memory_requirements(image) }; let alloc_desc = AllocationCreateDesc{ @@ -102,12 +97,6 @@ impl Allocator { } } -impl Drop for Allocator { - fn drop(&mut self) { - unsafe { ManuallyDrop::drop(&mut self.allocator) }; - } -} - pub struct Allocation { alloc: gpu_allocator::vulkan::Allocation, } diff --git a/src/objects/buffer.rs b/src/objects/buffer.rs index f97cf99..8d092d8 100644 --- a/src/objects/buffer.rs +++ b/src/objects/buffer.rs @@ -1,4 +1,6 @@ +use std::sync::Arc; use ash::vk; +use crate::objects::{id, SynchronizationGroup}; #[derive(Copy, Clone, Debug)] pub struct BufferSpec { @@ -15,37 +17,107 @@ impl BufferSpec { } } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct BufferRange { pub offset: u64, pub length: u64, } +/// Contains a description for a vulkan buffer. +/// +/// This only contains static information relevant to vulkan (i.e. size or supported usage flags). #[non_exhaustive] -pub struct BufferMeta { - -} - -#[non_exhaustive] -pub struct BufferCreateDesc { +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct BufferDescription { pub size: u64, pub usage_flags: vk::BufferUsageFlags, } -impl BufferCreateDesc { +impl BufferDescription { pub fn new_simple(size: u64, usage_flags: vk::BufferUsageFlags) -> Self { - BufferCreateDesc { size, usage_flags } + BufferDescription { size, usage_flags } + } +} + +/// Contains information about a vulkan buffer object. +/// +/// This expands the [`BufferDescription`] struct with information relevant for rosella (i.e. +/// synchronization group or other runtime information). Every instance of this struct will describe +/// only one specific buffer object. +pub struct BufferInfo { + desc: BufferDescription, + group: SynchronizationGroup, +} + +impl BufferInfo { + pub fn new(desc: BufferDescription, group: SynchronizationGroup) -> Self { + Self { + desc, + group + } + } + + pub fn get_description(&self) -> &BufferDescription { + &self.desc + } + + pub fn get_synchronization_group(&self) -> &SynchronizationGroup { + &self.group } } +/// Contains a description for a vulkan buffer. +/// +/// This only contains static information relevant to vulkan (i.e. range or format, however not the +/// source buffer as buffer views with different sources may have the same description). #[non_exhaustive] -pub struct BufferViewCreateDesc { +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct BufferViewDescription { pub format: &'static crate::objects::Format, pub range: BufferRange, } -impl BufferViewCreateDesc { +impl BufferViewDescription { pub fn new_simple(range: BufferRange, format: &'static crate::objects::Format) -> Self { Self { range, format } } +} + +/// Contains information about a vulkan buffer view object +/// +/// This expands the [`BufferViewDescription`] struct with information relevant for rosella (i.e. +/// the source buffer or other runtime information). Ever instance of this struct will describe +/// only one specific buffer view. +pub struct BufferViewInfo { + desc: BufferViewDescription, + source_buffer_id: id::BufferId, + source_buffer_info: Arc, +} + +impl BufferViewInfo { + pub fn new(desc: BufferViewDescription, source_buffer_id: id::BufferId, source_buffer_info: Arc) -> Self { + Self { + desc, + source_buffer_id, + source_buffer_info, + } + } + + pub fn get_description(&self) -> &BufferViewDescription { + &self.desc + } + + pub fn get_source_buffer_id(&self) -> id::BufferId { + self.source_buffer_id + } + + pub fn get_source_buffer_info(&self) -> &BufferInfo { + self.source_buffer_info.as_ref() + } + + /// Utility function to get the synchronization group for this buffer view. + /// Is equivalent to calling `get_source_buffer_info().get_synchronization_group()`. + pub fn get_synchronization_group(&self) -> &SynchronizationGroup { + &self.source_buffer_info.get_synchronization_group() + } } \ No newline at end of file diff --git a/src/objects/format.rs b/src/objects/format.rs index 3754b13..18c9a60 100644 --- a/src/objects/format.rs +++ b/src/objects/format.rs @@ -1,4 +1,5 @@ use std::fmt::{Debug, Formatter}; +use ash::vk; #[derive(Eq, Copy, Clone, Debug)] pub struct CompatibilityClass { @@ -108,9 +109,18 @@ pub struct Format { compatibility_class: CompatibilityClass, } -macro_rules! define_format { - ($name:ident, $compatibility_class:expr, $channel_count:expr) => { - pub const $name : Format = Format::new(ash::vk::Format::$name, $compatibility_class, $channel_count); +macro_rules! define_formats { + ($($name:ident, $compatibility_class:expr, $channel_count:expr);+) => { + pub const fn format_for(format: vk::Format) -> &'static Format { + match format { + $( + ash::vk::Format::$name => &Self::$name, + )+ + _ => { panic!("Unknown format!") } + } + } + + $(pub const $name : Format = Format::new(ash::vk::Format::$name, $compatibility_class, $channel_count);)+ } } @@ -131,224 +141,226 @@ impl Format { self.compatibility_class == other.compatibility_class } - define_format!(R4G4_UNORM_PACK8, CompatibilityClass::BIT8, 2); - define_format!(R4G4B4A4_UNORM_PACK16, CompatibilityClass::BIT16, 4); - define_format!(B4G4R4A4_UNORM_PACK16, CompatibilityClass::BIT16, 4); - define_format!(R5G6B5_UNORM_PACK16, CompatibilityClass::BIT16, 3); - define_format!(B5G6R5_UNORM_PACK16, CompatibilityClass::BIT16, 3); - define_format!(R5G5B5A1_UNORM_PACK16, CompatibilityClass::BIT16, 4); - define_format!(B5G5R5A1_UNORM_PACK16, CompatibilityClass::BIT16, 4); - define_format!(A1R5G5B5_UNORM_PACK16, CompatibilityClass::BIT16, 4); - define_format!(R8_UNORM, CompatibilityClass::BIT8, 1); - define_format!(R8_SNORM, CompatibilityClass::BIT8, 1); - define_format!(R8_USCALED, CompatibilityClass::BIT8, 1); - define_format!(R8_SSCALED, CompatibilityClass::BIT8, 1); - define_format!(R8_UINT, CompatibilityClass::BIT8, 1); - define_format!(R8_SINT, CompatibilityClass::BIT8, 1); - define_format!(R8_SRGB, CompatibilityClass::BIT8, 1); - define_format!(R8G8_UNORM, CompatibilityClass::BIT16, 2); - define_format!(R8G8_SNORM, CompatibilityClass::BIT16, 2); - define_format!(R8G8_USCALED, CompatibilityClass::BIT16, 2); - define_format!(R8G8_SSCALED, CompatibilityClass::BIT16, 2); - define_format!(R8G8_UINT, CompatibilityClass::BIT16, 2); - define_format!(R8G8_SINT, CompatibilityClass::BIT16, 2); - define_format!(R8G8_SRGB, CompatibilityClass::BIT16, 2); - define_format!(R8G8B8_UNORM, CompatibilityClass::BIT24, 3); - define_format!(R8G8B8_SNORM, CompatibilityClass::BIT24, 3); - define_format!(R8G8B8_USCALED, CompatibilityClass::BIT24, 3); - define_format!(R8G8B8_SSCALED, CompatibilityClass::BIT24, 3); - define_format!(R8G8B8_UINT, CompatibilityClass::BIT24, 3); - define_format!(R8G8B8_SINT, CompatibilityClass::BIT24, 3); - define_format!(R8G8B8_SRGB, CompatibilityClass::BIT24, 3); - define_format!(B8G8R8_UNORM, CompatibilityClass::BIT24, 3); - define_format!(B8G8R8_SNORM, CompatibilityClass::BIT24, 3); - define_format!(B8G8R8_USCALED, CompatibilityClass::BIT24, 3); - define_format!(B8G8R8_SSCALED, CompatibilityClass::BIT24, 3); - define_format!(B8G8R8_UINT, CompatibilityClass::BIT24, 3); - define_format!(B8G8R8_SINT, CompatibilityClass::BIT24, 3); - define_format!(B8G8R8_SRGB, CompatibilityClass::BIT24, 3); - define_format!(R8G8B8A8_UNORM, CompatibilityClass::BIT32, 4); - define_format!(R8G8B8A8_SNORM, CompatibilityClass::BIT32, 4); - define_format!(R8G8B8A8_USCALED, CompatibilityClass::BIT32, 4); - define_format!(R8G8B8A8_SSCALED, CompatibilityClass::BIT32, 4); - define_format!(R8G8B8A8_UINT, CompatibilityClass::BIT32, 4); - define_format!(R8G8B8A8_SINT, CompatibilityClass::BIT32, 4); - define_format!(R8G8B8A8_SRGB, CompatibilityClass::BIT32, 4); - define_format!(B8G8R8A8_UNORM, CompatibilityClass::BIT32, 4); - define_format!(B8G8R8A8_SNORM, CompatibilityClass::BIT32, 4); - define_format!(B8G8R8A8_USCALED, CompatibilityClass::BIT32, 4); - define_format!(B8G8R8A8_SSCALED, CompatibilityClass::BIT32, 4); - define_format!(B8G8R8A8_UINT, CompatibilityClass::BIT32, 4); - define_format!(B8G8R8A8_SINT, CompatibilityClass::BIT32, 4); - define_format!(B8G8R8A8_SRGB, CompatibilityClass::BIT32, 4); - define_format!(A8B8G8R8_UNORM_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A8B8G8R8_SNORM_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A8B8G8R8_USCALED_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A8B8G8R8_SSCALED_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A8B8G8R8_UINT_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A8B8G8R8_SINT_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A8B8G8R8_SRGB_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A2R10G10B10_UNORM_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A2R10G10B10_SNORM_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A2R10G10B10_USCALED_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A2R10G10B10_SSCALED_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A2R10G10B10_UINT_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A2R10G10B10_SINT_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A2B10G10R10_UNORM_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A2B10G10R10_SNORM_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A2B10G10R10_USCALED_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A2B10G10R10_SSCALED_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A2B10G10R10_UINT_PACK32, CompatibilityClass::BIT32, 4); - define_format!(A2B10G10R10_SINT_PACK32, CompatibilityClass::BIT32, 4); - define_format!(R16_UNORM, CompatibilityClass::BIT16, 1); - define_format!(R16_SNORM, CompatibilityClass::BIT16, 1); - define_format!(R16_USCALED, CompatibilityClass::BIT16, 1); - define_format!(R16_SSCALED, CompatibilityClass::BIT16, 1); - define_format!(R16_UINT, CompatibilityClass::BIT16, 1); - define_format!(R16_SINT, CompatibilityClass::BIT16, 1); - define_format!(R16_SFLOAT, CompatibilityClass::BIT16, 1); - define_format!(R16G16_UNORM, CompatibilityClass::BIT32, 2); - define_format!(R16G16_SNORM, CompatibilityClass::BIT32, 2); - define_format!(R16G16_USCALED, CompatibilityClass::BIT32, 2); - define_format!(R16G16_SSCALED, CompatibilityClass::BIT32, 2); - define_format!(R16G16_UINT, CompatibilityClass::BIT32, 2); - define_format!(R16G16_SINT, CompatibilityClass::BIT32, 2); - define_format!(R16G16_SFLOAT, CompatibilityClass::BIT32, 2); - define_format!(R16G16B16_UNORM, CompatibilityClass::BIT48, 3); - define_format!(R16G16B16_SNORM, CompatibilityClass::BIT48, 3); - define_format!(R16G16B16_USCALED, CompatibilityClass::BIT48, 3); - define_format!(R16G16B16_SSCALED, CompatibilityClass::BIT48, 3); - define_format!(R16G16B16_UINT, CompatibilityClass::BIT48, 3); - define_format!(R16G16B16_SINT, CompatibilityClass::BIT48, 3); - define_format!(R16G16B16_SFLOAT, CompatibilityClass::BIT48, 3); - define_format!(R16G16B16A16_UNORM, CompatibilityClass::BIT64, 4); - define_format!(R16G16B16A16_SNORM, CompatibilityClass::BIT64, 4); - define_format!(R16G16B16A16_USCALED, CompatibilityClass::BIT64, 4); - define_format!(R16G16B16A16_SSCALED, CompatibilityClass::BIT64, 4); - define_format!(R16G16B16A16_UINT, CompatibilityClass::BIT64, 4); - define_format!(R16G16B16A16_SINT, CompatibilityClass::BIT64, 4); - define_format!(R16G16B16A16_SFLOAT, CompatibilityClass::BIT64, 4); - define_format!(R32_UINT, CompatibilityClass::BIT32, 1); - define_format!(R32_SINT, CompatibilityClass::BIT32, 1); - define_format!(R32_SFLOAT, CompatibilityClass::BIT32, 1); - define_format!(R32G32_UINT, CompatibilityClass::BIT64, 2); - define_format!(R32G32_SINT, CompatibilityClass::BIT64, 2); - define_format!(R32G32_SFLOAT, CompatibilityClass::BIT64, 2); - define_format!(R32G32B32_UINT, CompatibilityClass::BIT96, 3); - define_format!(R32G32B32_SINT, CompatibilityClass::BIT96, 3); - define_format!(R32G32B32_SFLOAT, CompatibilityClass::BIT96, 3); - define_format!(R32G32B32A32_UINT, CompatibilityClass::BIT128, 4); - define_format!(R32G32B32A32_SINT, CompatibilityClass::BIT128, 4); - define_format!(R32G32B32A32_SFLOAT, CompatibilityClass::BIT128, 4); - define_format!(R64_UINT, CompatibilityClass::BIT64, 1); - define_format!(R64_SINT, CompatibilityClass::BIT64, 1); - define_format!(R64_SFLOAT, CompatibilityClass::BIT64, 1); - define_format!(R64G64_UINT, CompatibilityClass::BIT128, 2); - define_format!(R64G64_SINT, CompatibilityClass::BIT128, 2); - define_format!(R64G64_SFLOAT, CompatibilityClass::BIT128, 2); - define_format!(R64G64B64_UINT, CompatibilityClass::BIT192, 3); - define_format!(R64G64B64_SINT, CompatibilityClass::BIT192, 3); - define_format!(R64G64B64_SFLOAT, CompatibilityClass::BIT192, 3); - define_format!(R64G64B64A64_UINT, CompatibilityClass::BIT256, 4); - define_format!(R64G64B64A64_SINT, CompatibilityClass::BIT256, 4); - define_format!(R64G64B64A64_SFLOAT, CompatibilityClass::BIT256, 4); - define_format!(B10G11R11_UFLOAT_PACK32, CompatibilityClass::BIT32, 3); - define_format!(E5B9G9R9_UFLOAT_PACK32, CompatibilityClass::BIT32, 3); - define_format!(D16_UNORM, CompatibilityClass::D16, 1); - define_format!(X8_D24_UNORM_PACK32, CompatibilityClass::D24, 1); - define_format!(D32_SFLOAT, CompatibilityClass::D32, 1); - define_format!(S8_UINT, CompatibilityClass::S8, 1); - define_format!(D16_UNORM_S8_UINT, CompatibilityClass::D16S8, 2); - define_format!(D24_UNORM_S8_UINT, CompatibilityClass::D24S8, 2); - define_format!(D32_SFLOAT_S8_UINT, CompatibilityClass::D32S8, 2); - define_format!(BC1_RGB_UNORM_BLOCK, CompatibilityClass::BC1_RGB, 3); - define_format!(BC1_RGB_SRGB_BLOCK, CompatibilityClass::BC1_RGB, 3); - define_format!(BC1_RGBA_UNORM_BLOCK, CompatibilityClass::BC1_RGBA, 4); - define_format!(BC1_RGBA_SRGB_BLOCK, CompatibilityClass::BC1_RGBA, 4); - define_format!(BC2_UNORM_BLOCK, CompatibilityClass::BC2, 4); - define_format!(BC2_SRGB_BLOCK, CompatibilityClass::BC2, 4); - define_format!(BC3_UNORM_BLOCK, CompatibilityClass::BC3, 4); - define_format!(BC3_SRGB_BLOCK, CompatibilityClass::BC3, 4); - define_format!(BC4_UNORM_BLOCK, CompatibilityClass::BC4, 1); - define_format!(BC4_SNORM_BLOCK, CompatibilityClass::BC4, 1); - define_format!(BC5_UNORM_BLOCK, CompatibilityClass::BC5, 2); - define_format!(BC5_SNORM_BLOCK, CompatibilityClass::BC5, 2); - define_format!(BC6H_UFLOAT_BLOCK, CompatibilityClass::BC6H, 3); - define_format!(BC6H_SFLOAT_BLOCK, CompatibilityClass::BC6H, 3); - define_format!(BC7_UNORM_BLOCK, CompatibilityClass::BC7, 4); - define_format!(BC7_SRGB_BLOCK, CompatibilityClass::BC7, 4); - define_format!(ETC2_R8G8B8_UNORM_BLOCK, CompatibilityClass::ETC2_RGB, 3); - define_format!(ETC2_R8G8B8_SRGB_BLOCK, CompatibilityClass::ETC2_RGB, 3); - define_format!(ETC2_R8G8B8A1_UNORM_BLOCK, CompatibilityClass::ETC2_RGBA, 4); - define_format!(ETC2_R8G8B8A1_SRGB_BLOCK, CompatibilityClass::ETC2_RGBA, 4); - define_format!(ETC2_R8G8B8A8_UNORM_BLOCK, CompatibilityClass::ETC2_EAC_RGBA, 4); - define_format!(ETC2_R8G8B8A8_SRGB_BLOCK, CompatibilityClass::ETC2_EAC_RGBA, 4); - define_format!(EAC_R11_UNORM_BLOCK, CompatibilityClass::EAC_R, 1); - define_format!(EAC_R11_SNORM_BLOCK, CompatibilityClass::EAC_R, 1); - define_format!(EAC_R11G11_UNORM_BLOCK, CompatibilityClass::EAC_RG, 2); - define_format!(EAC_R11G11_SNORM_BLOCK, CompatibilityClass::EAC_RG, 2); - define_format!(ASTC_4X4_UNORM_BLOCK, CompatibilityClass::ASTC_4X4, 4); - define_format!(ASTC_4X4_SRGB_BLOCK, CompatibilityClass::ASTC_4X4, 4); - define_format!(ASTC_5X4_UNORM_BLOCK, CompatibilityClass::ASTC_5X4, 4); - define_format!(ASTC_5X4_SRGB_BLOCK, CompatibilityClass::ASTC_5X4, 4); - define_format!(ASTC_5X5_UNORM_BLOCK, CompatibilityClass::ASTC_5X5, 4); - define_format!(ASTC_5X5_SRGB_BLOCK, CompatibilityClass::ASTC_5X5, 4); - define_format!(ASTC_6X5_UNORM_BLOCK, CompatibilityClass::ASTC_6X5, 4); - define_format!(ASTC_6X5_SRGB_BLOCK, CompatibilityClass::ASTC_6X5, 4); - define_format!(ASTC_6X6_UNORM_BLOCK, CompatibilityClass::ASTC_6X6, 4); - define_format!(ASTC_6X6_SRGB_BLOCK, CompatibilityClass::ASTC_6X6, 4); - define_format!(ASTC_8X5_UNORM_BLOCK, CompatibilityClass::ASTC_8X5, 4); - define_format!(ASTC_8X5_SRGB_BLOCK, CompatibilityClass::ASTC_8X5, 4); - define_format!(ASTC_8X6_UNORM_BLOCK, CompatibilityClass::ASTC_8X6, 4); - define_format!(ASTC_8X6_SRGB_BLOCK, CompatibilityClass::ASTC_8X6, 4); - define_format!(ASTC_8X8_UNORM_BLOCK, CompatibilityClass::ASTC_8X8, 4); - define_format!(ASTC_8X8_SRGB_BLOCK, CompatibilityClass::ASTC_8X8, 4); - define_format!(ASTC_10X5_UNORM_BLOCK, CompatibilityClass::ASTC_10X5, 4); - define_format!(ASTC_10X5_SRGB_BLOCK, CompatibilityClass::ASTC_10X5, 4); - define_format!(ASTC_10X6_UNORM_BLOCK, CompatibilityClass::ASTC_10X6, 4); - define_format!(ASTC_10X6_SRGB_BLOCK, CompatibilityClass::ASTC_10X6, 4); - define_format!(ASTC_10X8_UNORM_BLOCK, CompatibilityClass::ASTC_10X8, 4); - define_format!(ASTC_10X8_SRGB_BLOCK, CompatibilityClass::ASTC_10X8, 4); - define_format!(ASTC_10X10_UNORM_BLOCK, CompatibilityClass::ASTC_10X10, 4); - define_format!(ASTC_10X10_SRGB_BLOCK, CompatibilityClass::ASTC_10X10, 4); - define_format!(ASTC_12X10_UNORM_BLOCK, CompatibilityClass::ASTC_12X10, 4); - define_format!(ASTC_12X10_SRGB_BLOCK, CompatibilityClass::ASTC_12X10, 4); - define_format!(ASTC_12X12_UNORM_BLOCK, CompatibilityClass::ASTC_12X12, 4); - define_format!(ASTC_12X12_SRGB_BLOCK, CompatibilityClass::ASTC_12X12, 4); - define_format!(G8B8G8R8_422_UNORM, CompatibilityClass::BIT32_G8B8G8R8, 4); - define_format!(B8G8R8G8_422_UNORM, CompatibilityClass::BIT32_B8G8R8G8, 4); - define_format!(G8_B8_R8_3PLANE_420_UNORM, CompatibilityClass::PLANE3_8BIT_420, 3); - define_format!(G8_B8R8_2PLANE_420_UNORM, CompatibilityClass::PLANE2_8BIT_420, 3); - define_format!(G8_B8_R8_3PLANE_422_UNORM, CompatibilityClass::PLANE3_8BIT_422, 3); - define_format!(G8_B8R8_2PLANE_422_UNORM, CompatibilityClass::PLANE2_8BIT_422, 3); - define_format!(G8_B8_R8_3PLANE_444_UNORM, CompatibilityClass::PLANE3_8BIT_444, 3); - define_format!(R10X6_UNORM_PACK16, CompatibilityClass::BIT16, 1); - define_format!(R10X6G10X6_UNORM_2PACK16, CompatibilityClass::BIT32, 2); - define_format!(R10X6G10X6B10X6A10X6_UNORM_4PACK16, CompatibilityClass::BIT64_R10G10B10A10, 4); - define_format!(G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, CompatibilityClass::BIT64_G10B10G10R10, 4); - define_format!(B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, CompatibilityClass::BIT64_B10G10R10G10, 4); - define_format!(G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, CompatibilityClass::PLANE3_10BIT_420, 3); - define_format!(G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, CompatibilityClass::PLANE2_10BIT_420, 3); - define_format!(G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, CompatibilityClass::PLANE3_10BIT_422, 3); - define_format!(G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, CompatibilityClass::PLANE2_10BIT_422, 3); - define_format!(G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, CompatibilityClass::PLANE3_10BIT_444, 3); - define_format!(R12X4_UNORM_PACK16, CompatibilityClass::BIT16, 1); - define_format!(R12X4G12X4_UNORM_2PACK16, CompatibilityClass::BIT32, 2); - define_format!(R12X4G12X4B12X4A12X4_UNORM_4PACK16, CompatibilityClass::BIT64_R12G12B12A12, 4); - define_format!(G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, CompatibilityClass::BIT64_G12B12G12R12, 4); - define_format!(B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, CompatibilityClass::BIT64_B12G12R12G12, 4); - define_format!(G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, CompatibilityClass::PLANE3_12BIT_420, 3); - define_format!(G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, CompatibilityClass::PLANE2_12BIT_420, 3); - define_format!(G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, CompatibilityClass::PLANE3_12BIT_422, 3); - define_format!(G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, CompatibilityClass::PLANE2_12BIT_422, 3); - define_format!(G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, CompatibilityClass::PLANE3_12BIT_444, 3); - define_format!(G16B16G16R16_422_UNORM, CompatibilityClass::BIT64_G16B16G16R16, 3); - define_format!(B16G16R16G16_422_UNORM, CompatibilityClass::BIT64_B16G16R16G16, 3); - define_format!(G16_B16_R16_3PLANE_420_UNORM, CompatibilityClass::PLANE3_16BIT_420, 3); - define_format!(G16_B16R16_2PLANE_420_UNORM, CompatibilityClass::PLANE2_16BIT_420, 3); - define_format!(G16_B16_R16_3PLANE_422_UNORM, CompatibilityClass::PLANE3_16BIT_422, 3); - define_format!(G16_B16R16_2PLANE_422_UNORM, CompatibilityClass::PLANE2_16BIT_422, 3); - define_format!(G16_B16_R16_3PLANE_444_UNORM, CompatibilityClass::PLANE3_16BIT_444, 3); + define_formats!( + R4G4_UNORM_PACK8, CompatibilityClass::BIT8, 2; + R4G4B4A4_UNORM_PACK16, CompatibilityClass::BIT16, 4; + B4G4R4A4_UNORM_PACK16, CompatibilityClass::BIT16, 4; + R5G6B5_UNORM_PACK16, CompatibilityClass::BIT16, 3; + B5G6R5_UNORM_PACK16, CompatibilityClass::BIT16, 3; + R5G5B5A1_UNORM_PACK16, CompatibilityClass::BIT16, 4; + B5G5R5A1_UNORM_PACK16, CompatibilityClass::BIT16, 4; + A1R5G5B5_UNORM_PACK16, CompatibilityClass::BIT16, 4; + R8_UNORM, CompatibilityClass::BIT8, 1; + R8_SNORM, CompatibilityClass::BIT8, 1; + R8_USCALED, CompatibilityClass::BIT8, 1; + R8_SSCALED, CompatibilityClass::BIT8, 1; + R8_UINT, CompatibilityClass::BIT8, 1; + R8_SINT, CompatibilityClass::BIT8, 1; + R8_SRGB, CompatibilityClass::BIT8, 1; + R8G8_UNORM, CompatibilityClass::BIT16, 2; + R8G8_SNORM, CompatibilityClass::BIT16, 2; + R8G8_USCALED, CompatibilityClass::BIT16, 2; + R8G8_SSCALED, CompatibilityClass::BIT16, 2; + R8G8_UINT, CompatibilityClass::BIT16, 2; + R8G8_SINT, CompatibilityClass::BIT16, 2; + R8G8_SRGB, CompatibilityClass::BIT16, 2; + R8G8B8_UNORM, CompatibilityClass::BIT24, 3; + R8G8B8_SNORM, CompatibilityClass::BIT24, 3; + R8G8B8_USCALED, CompatibilityClass::BIT24, 3; + R8G8B8_SSCALED, CompatibilityClass::BIT24, 3; + R8G8B8_UINT, CompatibilityClass::BIT24, 3; + R8G8B8_SINT, CompatibilityClass::BIT24, 3; + R8G8B8_SRGB, CompatibilityClass::BIT24, 3; + B8G8R8_UNORM, CompatibilityClass::BIT24, 3; + B8G8R8_SNORM, CompatibilityClass::BIT24, 3; + B8G8R8_USCALED, CompatibilityClass::BIT24, 3; + B8G8R8_SSCALED, CompatibilityClass::BIT24, 3; + B8G8R8_UINT, CompatibilityClass::BIT24, 3; + B8G8R8_SINT, CompatibilityClass::BIT24, 3; + B8G8R8_SRGB, CompatibilityClass::BIT24, 3; + R8G8B8A8_UNORM, CompatibilityClass::BIT32, 4; + R8G8B8A8_SNORM, CompatibilityClass::BIT32, 4; + R8G8B8A8_USCALED, CompatibilityClass::BIT32, 4; + R8G8B8A8_SSCALED, CompatibilityClass::BIT32, 4; + R8G8B8A8_UINT, CompatibilityClass::BIT32, 4; + R8G8B8A8_SINT, CompatibilityClass::BIT32, 4; + R8G8B8A8_SRGB, CompatibilityClass::BIT32, 4; + B8G8R8A8_UNORM, CompatibilityClass::BIT32, 4; + B8G8R8A8_SNORM, CompatibilityClass::BIT32, 4; + B8G8R8A8_USCALED, CompatibilityClass::BIT32, 4; + B8G8R8A8_SSCALED, CompatibilityClass::BIT32, 4; + B8G8R8A8_UINT, CompatibilityClass::BIT32, 4; + B8G8R8A8_SINT, CompatibilityClass::BIT32, 4; + B8G8R8A8_SRGB, CompatibilityClass::BIT32, 4; + A8B8G8R8_UNORM_PACK32, CompatibilityClass::BIT32, 4; + A8B8G8R8_SNORM_PACK32, CompatibilityClass::BIT32, 4; + A8B8G8R8_USCALED_PACK32, CompatibilityClass::BIT32, 4; + A8B8G8R8_SSCALED_PACK32, CompatibilityClass::BIT32, 4; + A8B8G8R8_UINT_PACK32, CompatibilityClass::BIT32, 4; + A8B8G8R8_SINT_PACK32, CompatibilityClass::BIT32, 4; + A8B8G8R8_SRGB_PACK32, CompatibilityClass::BIT32, 4; + A2R10G10B10_UNORM_PACK32, CompatibilityClass::BIT32, 4; + A2R10G10B10_SNORM_PACK32, CompatibilityClass::BIT32, 4; + A2R10G10B10_USCALED_PACK32, CompatibilityClass::BIT32, 4; + A2R10G10B10_SSCALED_PACK32, CompatibilityClass::BIT32, 4; + A2R10G10B10_UINT_PACK32, CompatibilityClass::BIT32, 4; + A2R10G10B10_SINT_PACK32, CompatibilityClass::BIT32, 4; + A2B10G10R10_UNORM_PACK32, CompatibilityClass::BIT32, 4; + A2B10G10R10_SNORM_PACK32, CompatibilityClass::BIT32, 4; + A2B10G10R10_USCALED_PACK32, CompatibilityClass::BIT32, 4; + A2B10G10R10_SSCALED_PACK32, CompatibilityClass::BIT32, 4; + A2B10G10R10_UINT_PACK32, CompatibilityClass::BIT32, 4; + A2B10G10R10_SINT_PACK32, CompatibilityClass::BIT32, 4; + R16_UNORM, CompatibilityClass::BIT16, 1; + R16_SNORM, CompatibilityClass::BIT16, 1; + R16_USCALED, CompatibilityClass::BIT16, 1; + R16_SSCALED, CompatibilityClass::BIT16, 1; + R16_UINT, CompatibilityClass::BIT16, 1; + R16_SINT, CompatibilityClass::BIT16, 1; + R16_SFLOAT, CompatibilityClass::BIT16, 1; + R16G16_UNORM, CompatibilityClass::BIT32, 2; + R16G16_SNORM, CompatibilityClass::BIT32, 2; + R16G16_USCALED, CompatibilityClass::BIT32, 2; + R16G16_SSCALED, CompatibilityClass::BIT32, 2; + R16G16_UINT, CompatibilityClass::BIT32, 2; + R16G16_SINT, CompatibilityClass::BIT32, 2; + R16G16_SFLOAT, CompatibilityClass::BIT32, 2; + R16G16B16_UNORM, CompatibilityClass::BIT48, 3; + R16G16B16_SNORM, CompatibilityClass::BIT48, 3; + R16G16B16_USCALED, CompatibilityClass::BIT48, 3; + R16G16B16_SSCALED, CompatibilityClass::BIT48, 3; + R16G16B16_UINT, CompatibilityClass::BIT48, 3; + R16G16B16_SINT, CompatibilityClass::BIT48, 3; + R16G16B16_SFLOAT, CompatibilityClass::BIT48, 3; + R16G16B16A16_UNORM, CompatibilityClass::BIT64, 4; + R16G16B16A16_SNORM, CompatibilityClass::BIT64, 4; + R16G16B16A16_USCALED, CompatibilityClass::BIT64, 4; + R16G16B16A16_SSCALED, CompatibilityClass::BIT64, 4; + R16G16B16A16_UINT, CompatibilityClass::BIT64, 4; + R16G16B16A16_SINT, CompatibilityClass::BIT64, 4; + R16G16B16A16_SFLOAT, CompatibilityClass::BIT64, 4; + R32_UINT, CompatibilityClass::BIT32, 1; + R32_SINT, CompatibilityClass::BIT32, 1; + R32_SFLOAT, CompatibilityClass::BIT32, 1; + R32G32_UINT, CompatibilityClass::BIT64, 2; + R32G32_SINT, CompatibilityClass::BIT64, 2; + R32G32_SFLOAT, CompatibilityClass::BIT64, 2; + R32G32B32_UINT, CompatibilityClass::BIT96, 3; + R32G32B32_SINT, CompatibilityClass::BIT96, 3; + R32G32B32_SFLOAT, CompatibilityClass::BIT96, 3; + R32G32B32A32_UINT, CompatibilityClass::BIT128, 4; + R32G32B32A32_SINT, CompatibilityClass::BIT128, 4; + R32G32B32A32_SFLOAT, CompatibilityClass::BIT128, 4; + R64_UINT, CompatibilityClass::BIT64, 1; + R64_SINT, CompatibilityClass::BIT64, 1; + R64_SFLOAT, CompatibilityClass::BIT64, 1; + R64G64_UINT, CompatibilityClass::BIT128, 2; + R64G64_SINT, CompatibilityClass::BIT128, 2; + R64G64_SFLOAT, CompatibilityClass::BIT128, 2; + R64G64B64_UINT, CompatibilityClass::BIT192, 3; + R64G64B64_SINT, CompatibilityClass::BIT192, 3; + R64G64B64_SFLOAT, CompatibilityClass::BIT192, 3; + R64G64B64A64_UINT, CompatibilityClass::BIT256, 4; + R64G64B64A64_SINT, CompatibilityClass::BIT256, 4; + R64G64B64A64_SFLOAT, CompatibilityClass::BIT256, 4; + B10G11R11_UFLOAT_PACK32, CompatibilityClass::BIT32, 3; + E5B9G9R9_UFLOAT_PACK32, CompatibilityClass::BIT32, 3; + D16_UNORM, CompatibilityClass::D16, 1; + X8_D24_UNORM_PACK32, CompatibilityClass::D24, 1; + D32_SFLOAT, CompatibilityClass::D32, 1; + S8_UINT, CompatibilityClass::S8, 1; + D16_UNORM_S8_UINT, CompatibilityClass::D16S8, 2; + D24_UNORM_S8_UINT, CompatibilityClass::D24S8, 2; + D32_SFLOAT_S8_UINT, CompatibilityClass::D32S8, 2; + BC1_RGB_UNORM_BLOCK, CompatibilityClass::BC1_RGB, 3; + BC1_RGB_SRGB_BLOCK, CompatibilityClass::BC1_RGB, 3; + BC1_RGBA_UNORM_BLOCK, CompatibilityClass::BC1_RGBA, 4; + BC1_RGBA_SRGB_BLOCK, CompatibilityClass::BC1_RGBA, 4; + BC2_UNORM_BLOCK, CompatibilityClass::BC2, 4; + BC2_SRGB_BLOCK, CompatibilityClass::BC2, 4; + BC3_UNORM_BLOCK, CompatibilityClass::BC3, 4; + BC3_SRGB_BLOCK, CompatibilityClass::BC3, 4; + BC4_UNORM_BLOCK, CompatibilityClass::BC4, 1; + BC4_SNORM_BLOCK, CompatibilityClass::BC4, 1; + BC5_UNORM_BLOCK, CompatibilityClass::BC5, 2; + BC5_SNORM_BLOCK, CompatibilityClass::BC5, 2; + BC6H_UFLOAT_BLOCK, CompatibilityClass::BC6H, 3; + BC6H_SFLOAT_BLOCK, CompatibilityClass::BC6H, 3; + BC7_UNORM_BLOCK, CompatibilityClass::BC7, 4; + BC7_SRGB_BLOCK, CompatibilityClass::BC7, 4; + ETC2_R8G8B8_UNORM_BLOCK, CompatibilityClass::ETC2_RGB, 3; + ETC2_R8G8B8_SRGB_BLOCK, CompatibilityClass::ETC2_RGB, 3; + ETC2_R8G8B8A1_UNORM_BLOCK, CompatibilityClass::ETC2_RGBA, 4; + ETC2_R8G8B8A1_SRGB_BLOCK, CompatibilityClass::ETC2_RGBA, 4; + ETC2_R8G8B8A8_UNORM_BLOCK, CompatibilityClass::ETC2_EAC_RGBA, 4; + ETC2_R8G8B8A8_SRGB_BLOCK, CompatibilityClass::ETC2_EAC_RGBA, 4; + EAC_R11_UNORM_BLOCK, CompatibilityClass::EAC_R, 1; + EAC_R11_SNORM_BLOCK, CompatibilityClass::EAC_R, 1; + EAC_R11G11_UNORM_BLOCK, CompatibilityClass::EAC_RG, 2; + EAC_R11G11_SNORM_BLOCK, CompatibilityClass::EAC_RG, 2; + ASTC_4X4_UNORM_BLOCK, CompatibilityClass::ASTC_4X4, 4; + ASTC_4X4_SRGB_BLOCK, CompatibilityClass::ASTC_4X4, 4; + ASTC_5X4_UNORM_BLOCK, CompatibilityClass::ASTC_5X4, 4; + ASTC_5X4_SRGB_BLOCK, CompatibilityClass::ASTC_5X4, 4; + ASTC_5X5_UNORM_BLOCK, CompatibilityClass::ASTC_5X5, 4; + ASTC_5X5_SRGB_BLOCK, CompatibilityClass::ASTC_5X5, 4; + ASTC_6X5_UNORM_BLOCK, CompatibilityClass::ASTC_6X5, 4; + ASTC_6X5_SRGB_BLOCK, CompatibilityClass::ASTC_6X5, 4; + ASTC_6X6_UNORM_BLOCK, CompatibilityClass::ASTC_6X6, 4; + ASTC_6X6_SRGB_BLOCK, CompatibilityClass::ASTC_6X6, 4; + ASTC_8X5_UNORM_BLOCK, CompatibilityClass::ASTC_8X5, 4; + ASTC_8X5_SRGB_BLOCK, CompatibilityClass::ASTC_8X5, 4; + ASTC_8X6_UNORM_BLOCK, CompatibilityClass::ASTC_8X6, 4; + ASTC_8X6_SRGB_BLOCK, CompatibilityClass::ASTC_8X6, 4; + ASTC_8X8_UNORM_BLOCK, CompatibilityClass::ASTC_8X8, 4; + ASTC_8X8_SRGB_BLOCK, CompatibilityClass::ASTC_8X8, 4; + ASTC_10X5_UNORM_BLOCK, CompatibilityClass::ASTC_10X5, 4; + ASTC_10X5_SRGB_BLOCK, CompatibilityClass::ASTC_10X5, 4; + ASTC_10X6_UNORM_BLOCK, CompatibilityClass::ASTC_10X6, 4; + ASTC_10X6_SRGB_BLOCK, CompatibilityClass::ASTC_10X6, 4; + ASTC_10X8_UNORM_BLOCK, CompatibilityClass::ASTC_10X8, 4; + ASTC_10X8_SRGB_BLOCK, CompatibilityClass::ASTC_10X8, 4; + ASTC_10X10_UNORM_BLOCK, CompatibilityClass::ASTC_10X10, 4; + ASTC_10X10_SRGB_BLOCK, CompatibilityClass::ASTC_10X10, 4; + ASTC_12X10_UNORM_BLOCK, CompatibilityClass::ASTC_12X10, 4; + ASTC_12X10_SRGB_BLOCK, CompatibilityClass::ASTC_12X10, 4; + ASTC_12X12_UNORM_BLOCK, CompatibilityClass::ASTC_12X12, 4; + ASTC_12X12_SRGB_BLOCK, CompatibilityClass::ASTC_12X12, 4; + G8B8G8R8_422_UNORM, CompatibilityClass::BIT32_G8B8G8R8, 4; + B8G8R8G8_422_UNORM, CompatibilityClass::BIT32_B8G8R8G8, 4; + G8_B8_R8_3PLANE_420_UNORM, CompatibilityClass::PLANE3_8BIT_420, 3; + G8_B8R8_2PLANE_420_UNORM, CompatibilityClass::PLANE2_8BIT_420, 3; + G8_B8_R8_3PLANE_422_UNORM, CompatibilityClass::PLANE3_8BIT_422, 3; + G8_B8R8_2PLANE_422_UNORM, CompatibilityClass::PLANE2_8BIT_422, 3; + G8_B8_R8_3PLANE_444_UNORM, CompatibilityClass::PLANE3_8BIT_444, 3; + R10X6_UNORM_PACK16, CompatibilityClass::BIT16, 1; + R10X6G10X6_UNORM_2PACK16, CompatibilityClass::BIT32, 2; + R10X6G10X6B10X6A10X6_UNORM_4PACK16, CompatibilityClass::BIT64_R10G10B10A10, 4; + G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, CompatibilityClass::BIT64_G10B10G10R10, 4; + B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, CompatibilityClass::BIT64_B10G10R10G10, 4; + G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, CompatibilityClass::PLANE3_10BIT_420, 3; + G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, CompatibilityClass::PLANE2_10BIT_420, 3; + G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, CompatibilityClass::PLANE3_10BIT_422, 3; + G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, CompatibilityClass::PLANE2_10BIT_422, 3; + G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, CompatibilityClass::PLANE3_10BIT_444, 3; + R12X4_UNORM_PACK16, CompatibilityClass::BIT16, 1; + R12X4G12X4_UNORM_2PACK16, CompatibilityClass::BIT32, 2; + R12X4G12X4B12X4A12X4_UNORM_4PACK16, CompatibilityClass::BIT64_R12G12B12A12, 4; + G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, CompatibilityClass::BIT64_G12B12G12R12, 4; + B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, CompatibilityClass::BIT64_B12G12R12G12, 4; + G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, CompatibilityClass::PLANE3_12BIT_420, 3; + G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, CompatibilityClass::PLANE2_12BIT_420, 3; + G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, CompatibilityClass::PLANE3_12BIT_422, 3; + G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, CompatibilityClass::PLANE2_12BIT_422, 3; + G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, CompatibilityClass::PLANE3_12BIT_444, 3; + G16B16G16R16_422_UNORM, CompatibilityClass::BIT64_G16B16G16R16, 3; + B16G16R16G16_422_UNORM, CompatibilityClass::BIT64_B16G16R16G16, 3; + G16_B16_R16_3PLANE_420_UNORM, CompatibilityClass::PLANE3_16BIT_420, 3; + G16_B16R16_2PLANE_420_UNORM, CompatibilityClass::PLANE2_16BIT_420, 3; + G16_B16_R16_3PLANE_422_UNORM, CompatibilityClass::PLANE3_16BIT_422, 3; + G16_B16R16_2PLANE_422_UNORM, CompatibilityClass::PLANE2_16BIT_422, 3; + G16_B16_R16_3PLANE_444_UNORM, CompatibilityClass::PLANE3_16BIT_444, 3 + ); } impl PartialEq for Format { diff --git a/src/objects/id.rs b/src/objects/id.rs index f1ef53e..f706a2b 100644 --- a/src/objects/id.rs +++ b/src/objects/id.rs @@ -1,6 +1,48 @@ use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; -use crate::util::id::{GlobalId, LocalId, UUID}; +use std::num::NonZeroU64; +use std::sync::atomic::{AtomicU64, Ordering}; + +use ash::vk; + +/// An identifier for object sets +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ObjectSetId(NonZeroU64); + +static NEXT_OBJECT_SET_ID : AtomicU64 = AtomicU64::new(1); + +impl ObjectSetId { + const OBJECT_SET_ID_MAX : u64 = (1u64 << 40u32) - 1u64; + + /// Creates a new unique object set id + pub fn new() -> Self { + let next = NEXT_OBJECT_SET_ID.fetch_add(1, Ordering::Relaxed); + if next > Self::OBJECT_SET_ID_MAX { + panic!("ObjectSetId overflow"); + } + + Self(NonZeroU64::new(next).unwrap()) + } + + fn from_raw(raw: u64) -> Self { + if raw > Self::OBJECT_SET_ID_MAX { + panic!("Value passed to ObjectSetId::from_raw is out of bounds"); + } + + Self(NonZeroU64::new(raw).unwrap()) + } + + /// Returns the raw 64bit value of the id + pub fn get_raw(&self) -> u64 { + self.0.get() + } +} + +impl Debug for ObjectSetId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("ObjectSetId({:#010X})", self.0.get())) + } +} pub struct ObjectType; @@ -12,9 +54,11 @@ impl ObjectType { Self::BUFFER_VIEW => "BufferView", Self::IMAGE => "Image", Self::IMAGE_VIEW => "ImageView", - Self::BINARY_SEMAPHORE => "BinarySemaphore", - Self::TIMELINE_SEMAPHORE => "TimelineSemaphore", + Self::SEMAPHORE => "Semaphore", Self::EVENT => "Event", + Self::FENCE => "Fence", + Self::SURFACE => "Surface", + Self::SWAPCHAIN => "Swapchain", _ => "Invalid", } } @@ -25,64 +69,57 @@ impl ObjectType { pub const BUFFER_VIEW: u8 = 2u8; pub const IMAGE: u8 = 3u8; pub const IMAGE_VIEW: u8 = 4u8; - pub const BINARY_SEMAPHORE: u8 = 5u8; - pub const TIMELINE_SEMAPHORE: u8 = 6u8; - pub const EVENT: u8 = 7u8; + pub const SEMAPHORE: u8 = 5u8; + pub const EVENT: u8 = 6u8; + pub const FENCE: u8 = 7u8; + pub const SURFACE: u8 = 8u8; + pub const SWAPCHAIN: u8 = 9u8; } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct ObjectId(UUID); +pub struct ObjectId(NonZeroU64); impl ObjectId { - pub const INDEX_BITS: u32 = 56u32; - const INDEX_OFFSET: u32 = 0u32; - pub const INDEX_MAX: u64 = (1u64 << Self::INDEX_BITS) - 1u64; - const INDEX_MASK: u64 = Self::INDEX_MAX << Self::INDEX_OFFSET; - - const TYPE_OFFSET: u32 = Self::INDEX_OFFSET + Self::INDEX_BITS; - const TYPE_MASK: u64 = (u8::MAX as u64) << Self::TYPE_OFFSET; + const SET_ID_BITS: u32 = 40u32; + const SET_ID_OFFSET: u32 = 0u32; + const SET_ID_MAX: u64 = (1u64 << Self::SET_ID_BITS) - 1u64; + const SET_ID_MASK: u64 = Self::SET_ID_MAX << Self::SET_ID_OFFSET; - fn make(global_id: GlobalId, index: u64, object_type: u8) -> Self { - if index > Self::INDEX_MAX { - panic!("Local id out of range"); - } + const INDEX_OFFSET: u32 = 48u32; + const INDEX_MAX: u64 = u16::MAX as u64; + const INDEX_MASK: u64 = Self::INDEX_MAX << Self::INDEX_OFFSET; - let local = (index << Self::INDEX_OFFSET) | ((object_type as u64) << Self::TYPE_OFFSET); + const TYPE_OFFSET: u32 = 40u32; + const TYPE_MAX: u64 = u8::MAX as u64; + const TYPE_MASK: u64 = Self::TYPE_MAX << Self::TYPE_OFFSET; - Self(UUID{ - global: global_id, - local: LocalId::from_raw(local), - }) - } + fn make(set_id: ObjectSetId, index: u16, object_type: u8) -> Self { + let id = (set_id.get_raw() << Self::SET_ID_OFFSET) | ((index as u64) << Self::INDEX_OFFSET) | ((object_type as u64) << Self::TYPE_OFFSET); - pub const fn get_global_id(&self) -> GlobalId { - self.0.global + Self(NonZeroU64::new(id).unwrap()) } - pub const fn get_local_id(&self) -> LocalId { - self.0.local + pub fn get_set_id(&self) -> ObjectSetId { + ObjectSetId::from_raw((self.0.get() & Self::SET_ID_MASK) >> Self::SET_ID_OFFSET) } - pub const fn get_index(&self) -> u64 { - (self.0.local.get_raw() & Self::INDEX_MASK) >> Self::INDEX_OFFSET + pub const fn get_index(&self) -> u16 { + ((self.0.get() & Self::INDEX_MASK) >> Self::INDEX_OFFSET) as u16 } pub const fn get_type(&self) -> u8 { - ((self.0.local.get_raw() & Self::TYPE_MASK) >> Self::TYPE_OFFSET) as u8 + ((self.0.get() & Self::TYPE_MASK) >> Self::TYPE_OFFSET) as u8 } + /// Converts the id to a generic id pub const fn as_generic(&self) -> ObjectId<{ ObjectType::GENERIC }> { ObjectId::<{ ObjectType::GENERIC }>(self.0) } } -impl Into for ObjectId { - fn into(self) -> UUID { - self.0 - } -} - impl ObjectId<{ ObjectType::GENERIC }> { + /// Attempts to cast a generic object id to a specific type. If the generic id is not of the + /// correct type `None` is returned. pub const fn downcast(self) -> Option> { if self.get_type() == TRG { Some(ObjectId::(self.0)) @@ -94,11 +131,7 @@ impl ObjectId<{ ObjectType::GENERIC }> { impl Debug for ObjectId { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ObjectId") - .field("type", &self.get_type()) - .field("local_id", &self.get_local_id()) - .field("global_id", &self.get_global_id()) - .finish() + f.write_fmt(format_args!("ObjectId(Set: {:#010X}, Index: {:#04X}, Type: {})", self.get_set_id().get_raw(), self.get_index(), self.get_type())) } } @@ -108,53 +141,57 @@ impl Hash for ObjectId { } } -impl ObjectId<{ ObjectType::BUFFER }> { - pub fn new(global_id: GlobalId, index: u64) -> Self { - Self::make(global_id, index, ObjectType::BUFFER) - } +/// Utility trait used to get the id type for a handle type +pub trait ObjectHandleType { + type Id; } -impl ObjectId<{ ObjectType::BUFFER_VIEW }> { - pub fn new(global_id: GlobalId, index: u64) -> Self { - Self::make(global_id, index, ObjectType::BUFFER_VIEW) - } -} +/// Utility trait used to get the handle type for an id type +pub trait ObjectIdType { + type Handle: vk::Handle; -impl ObjectId<{ ObjectType::IMAGE }> { - pub fn new(global_id: GlobalId, index: u64) -> Self { - Self::make(global_id, index, ObjectType::IMAGE) - } + fn as_generic(&self) -> GenericId; } -impl ObjectId<{ ObjectType::IMAGE_VIEW }> { - pub fn new(global_id: GlobalId, index: u64) -> Self { - Self::make(global_id, index, ObjectType::IMAGE_VIEW) - } -} +macro_rules! make_object_id { + ($value:expr, $handle_type:ty) => { + impl ObjectId<{$value}> { + pub fn new(set_id: ObjectSetId, index: u16) -> Self { + Self::make(set_id, index, $value) + } + } -impl ObjectId<{ ObjectType::BINARY_SEMAPHORE }> { - pub fn new(global_id: GlobalId, index: u64) -> Self { - Self::make(global_id, index, ObjectType::BINARY_SEMAPHORE) - } -} + impl ObjectIdType for ObjectId<{$value}> { + type Handle = $handle_type; -impl ObjectId<{ ObjectType::TIMELINE_SEMAPHORE }> { - pub fn new(global_id: GlobalId, index: u64) -> Self { - Self::make(global_id, index, ObjectType::TIMELINE_SEMAPHORE) - } -} + fn as_generic(&self) -> GenericId { + self.as_generic() + } + } -impl ObjectId<{ ObjectType::EVENT }> { - pub fn new(global_id: GlobalId, index: u64) -> Self { - Self::make(global_id, index, ObjectType::EVENT) + impl ObjectHandleType for $handle_type { + type Id = ObjectId<{$value}>; + } } } +make_object_id!(ObjectType::BUFFER, vk::Buffer); +make_object_id!(ObjectType::BUFFER_VIEW, vk::BufferView); +make_object_id!(ObjectType::IMAGE, vk::Image); +make_object_id!(ObjectType::IMAGE_VIEW, vk::ImageView); +make_object_id!(ObjectType::SEMAPHORE, vk::Semaphore); +make_object_id!(ObjectType::EVENT, vk::Event); +make_object_id!(ObjectType::FENCE, vk::Fence); +make_object_id!(ObjectType::SURFACE, vk::SurfaceKHR); +make_object_id!(ObjectType::SWAPCHAIN, vk::SwapchainKHR); + pub type GenericId = ObjectId<{ ObjectType::GENERIC }>; pub type BufferId = ObjectId<{ ObjectType::BUFFER }>; pub type BufferViewId = ObjectId<{ ObjectType::BUFFER_VIEW }>; pub type ImageId = ObjectId<{ ObjectType::IMAGE }>; pub type ImageViewId = ObjectId<{ ObjectType::IMAGE_VIEW }>; -pub type BinarySemaphoreId = ObjectId<{ ObjectType::BINARY_SEMAPHORE }>; -pub type TimelineSemaphoreId = ObjectId<{ ObjectType::TIMELINE_SEMAPHORE }>; -pub type EventId = ObjectId<{ ObjectType::EVENT }>; \ No newline at end of file +pub type SemaphoreId = ObjectId<{ ObjectType::SEMAPHORE }>; +pub type EventId = ObjectId<{ ObjectType::EVENT }>; +pub type FenceId = ObjectId<{ ObjectType::FENCE }>; +pub type SurfaceId = ObjectId<{ ObjectType::SURFACE }>; +pub type SwapchainId = ObjectId<{ ObjectType::SWAPCHAIN }>; \ No newline at end of file diff --git a/src/objects/image.rs b/src/objects/image.rs index e8dc117..f476ad8 100644 --- a/src/objects/image.rs +++ b/src/objects/image.rs @@ -1,8 +1,10 @@ use std::fmt::Debug; +use std::sync::Arc; use ash::vk; +use crate::objects::{Format, id, SynchronizationGroup}; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ImageSize { Type1D { width: u32, mip_levels: u32, array_layers: u32 }, Type2D { width: u32, height: u32, mip_levels: u32, array_layers: u32 }, @@ -111,7 +113,7 @@ impl ImageSize { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct ImageSpec { pub format: &'static crate::objects::Format, pub sample_count: ash::vk::SampleCountFlags, @@ -144,7 +146,7 @@ impl ImageSpec { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct ImageSubresourceRange { pub aspect_mask: ash::vk::ImageAspectFlags, pub base_mip_level: u32, @@ -165,26 +167,136 @@ impl ImageSubresourceRange { } } +/// Contains a description for a vulkan image. +/// +/// This only contains static information relevant to vulkan (i.e. size or supported usage flags). #[non_exhaustive] -pub struct ImageMeta { - -} - -#[non_exhaustive] -pub struct ImageCreateDesc { +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct ImageDescription { pub spec: ImageSpec, pub usage_flags: vk::ImageUsageFlags, } -impl ImageCreateDesc { +impl ImageDescription { pub fn new_simple(spec: ImageSpec, usage: vk::ImageUsageFlags) -> Self { Self{ spec, usage_flags: usage } } } -pub struct ImageViewCreateDesc { +/// Contains information about a vulkan image object. +/// +/// This expands the [`ImageDescription`] struct with information relevant for rosella (i.e. +/// synchronization group or other runtime information). Every instance of this struct will describe +/// only one specific image object. +pub struct ImageInfo { + desc: ImageDescription, + group: SynchronizationGroup, +} + +impl ImageInfo { + pub fn new(desc: ImageDescription, group: SynchronizationGroup) -> Self { + Self { + desc, + group, + } + } + + pub fn get_description(&self) -> &ImageDescription { + &self.desc + } + + pub fn get_synchronization_group(&self) -> &SynchronizationGroup { + &self.group + } +} + +/// Contains a description for a vulkan image view. +/// +/// This only contains static information relevant to vulkan (i.e. range or format, however not the +/// source image as image views with different sources may have the same description). +#[non_exhaustive] +#[derive(Copy, Clone, Debug)] +pub struct ImageViewDescription { pub view_type: vk::ImageViewType, - pub format: &'static crate::objects::Format, + pub format: &'static Format, pub components: vk::ComponentMapping, pub subresource_range: ImageSubresourceRange, +} + +impl ImageViewDescription { + /// Creates a image view description with identity component mapping and subresource range + /// covering all mip levels and array layers. + pub fn make_full(view_type: vk::ImageViewType, format: &'static Format, aspect_mask: vk::ImageAspectFlags) -> Self { + Self { + view_type, + format, + components: vk::ComponentMapping { + r: vk::ComponentSwizzle::IDENTITY, + g: vk::ComponentSwizzle::IDENTITY, + b: vk::ComponentSwizzle::IDENTITY, + a: vk::ComponentSwizzle::IDENTITY + }, + subresource_range: ImageSubresourceRange { + aspect_mask, + base_mip_level: 0, + mip_level_count: vk::REMAINING_MIP_LEVELS, + base_array_layer: 0, + array_layer_count: vk::REMAINING_ARRAY_LAYERS, + } + } + } + + /// Creates a image view description with identity component mapping + pub fn make_range(view_type: vk::ImageViewType, format: &'static Format, subresource_range: ImageSubresourceRange) -> Self { + Self { + view_type, + format, + components: vk::ComponentMapping { + r: vk::ComponentSwizzle::IDENTITY, + g: vk::ComponentSwizzle::IDENTITY, + b: vk::ComponentSwizzle::IDENTITY, + a: vk::ComponentSwizzle::IDENTITY + }, + subresource_range + } + } +} + +/// Contains information about a vulkan image view object +/// +/// This expands the [`ImageViewDescription`] struct with information relevant for rosella (i.e. +/// the source image or other runtime information). Every instance of this struct will describe +/// only one specific image view object. +pub struct ImageViewInfo { + desc: ImageViewDescription, + source_image_id: id::ImageId, + source_image_info: Arc, +} + +impl ImageViewInfo { + pub fn new(desc: ImageViewDescription, source_image_id: id::ImageId, source_image_info: Arc) -> Self { + Self { + desc, + source_image_id, + source_image_info + } + } + + pub fn get_description(&self) -> &ImageViewDescription { + &self.desc + } + + pub fn get_source_image_id(&self) -> id::ImageId { + self.source_image_id + } + + pub fn get_source_image_info(&self) -> &ImageInfo { + self.source_image_info.as_ref() + } + + /// Utility function to get the synchronization group for this image view. + /// Is equivalent to calling `get_source_image_info().get_synchronization_group()`. + pub fn get_synchronization_group(&self) -> &SynchronizationGroup { + self.source_image_info.get_synchronization_group() + } } \ No newline at end of file diff --git a/src/objects/manager/mod.rs b/src/objects/manager/mod.rs deleted file mode 100644 index f00fea9..0000000 --- a/src/objects/manager/mod.rs +++ /dev/null @@ -1,629 +0,0 @@ -//! Management of vulkan objects. -//! -//! Contains structs and enums to manage creation, access to and destruction of vulkan objects. -//! -//! Access to objects is controlled using synchronization groups. All objects belonging to a -//! synchronization group are accessed as one unit protected by a single timeline semaphore. -//! -//! Allocation and destruction of objects is managed through object sets. A objects set is a -//! collection of objects that have the same lifetime. All objects are created when creating the set -//! and all objects are destroyed only when the entire set is destroyed. All objects of a set -//! belong to the same synchronization group. -//! -//! Both synchronization groups as well as objects sets are managed by smart pointers eliminating -//! the need for manual lifetime management. Object sets keep a reference to their synchronization -//! group internally meaning that if a synchronization group is needed only for a single objects set -//! it suffices to keep the object set alive to also ensure the synchronization group stays alive. -//! -//! Multiple object sets can be accessed in a sequentially consistent manner by using -//! synchronization group sets. This is required to prevent deadlock situations when trying to -//! access multiple sets for the same operation. - -pub(super) mod synchronization_group; -pub(super) mod object_set; - -mod allocator; - -use std::sync::Arc; - -use ash::vk; - -use synchronization_group::*; -use object_set::*; -use crate::objects::buffer::{BufferCreateDesc, BufferViewCreateDesc}; -use crate::objects::id; -use crate::objects::image::{ImageCreateDesc, ImageViewCreateDesc}; -use crate::objects::manager::allocator::*; -use crate::util::slice_splitter::Splitter; - -#[derive(Debug)] -enum ObjectCreateError { - Vulkan(vk::Result), - Allocation(AllocationError), - InvalidReference, -} - -impl<'s> From for ObjectCreateError { - fn from(err: vk::Result) -> Self { - ObjectCreateError::Vulkan(err) - } -} - -impl<'s> From for ObjectCreateError { - fn from(err: AllocationError) -> Self { - ObjectCreateError::Allocation(err) - } -} - -struct BufferCreateMetadata<'a> { - handle: vk::Buffer, - allocation: Option, - desc: &'a BufferRequestDescription, -} - -struct BufferViewCreateMetadata<'a> { - handle: vk::BufferView, - desc: &'a BufferViewRequestDescription, -} - -struct ImageCreateMetadata<'a> { - handle: vk::Image, - allocation: Option, - desc: &'a ImageRequestDescription, -} - -struct ImageViewCreateMetadata<'a> { - handle: vk::ImageView, - desc: &'a ImageViewRequestDescription, -} - -/// Internal struct used during object creation -enum ObjectCreateMetadata<'a> { - Buffer(BufferCreateMetadata<'a>), - BufferView(BufferViewCreateMetadata<'a>), - Image(ImageCreateMetadata<'a>), - ImageView(ImageViewCreateMetadata<'a>), -} - -impl<'a> ObjectCreateMetadata<'a> { - fn make_buffer(desc: &'a BufferRequestDescription) -> Self { - Self::Buffer(BufferCreateMetadata{ - handle: vk::Buffer::null(), - allocation: None, - desc - }) - } - - fn make_buffer_view(desc: &'a BufferViewRequestDescription) -> Self { - Self::BufferView(BufferViewCreateMetadata{ - handle: vk::BufferView::null(), - desc - }) - } - - fn make_image(desc: &'a ImageRequestDescription) -> Self { - Self::Image(ImageCreateMetadata{ - handle: vk::Image::null(), - allocation: None, - desc - }) - } - - fn make_image_view(desc: &'a ImageViewRequestDescription) -> Self { - Self::ImageView(ImageViewCreateMetadata{ - handle: vk::ImageView::null(), - desc - }) - } -} - -// Internal implementation of the object manager -struct ObjectManagerImpl { - device: crate::rosella::DeviceContext, - allocator: Allocator, -} - -impl ObjectManagerImpl { - fn new(device: crate::rosella::DeviceContext) -> Self { - let allocator = Allocator::new(device.clone()); - - Self{ - device, - allocator, - } - } - - /// Creates a timeline semaphore for use in a synchronization group - fn create_timeline_semaphore(&self, initial_value: u64) -> vk::Semaphore { - let mut timeline_info = vk::SemaphoreTypeCreateInfo::builder() - .semaphore_type(vk::SemaphoreType::TIMELINE) - .initial_value(initial_value); - let info = vk::SemaphoreCreateInfo::builder().push_next(&mut timeline_info); - - unsafe { - self.device.vk().create_semaphore(&info.build(), None).unwrap() - } - } - - /// Destroys a semaphore previously created using [`ObjectManagerImpl::create_timeline_semaphore`] - fn destroy_semaphore(&self, semaphore: vk::Semaphore) { - unsafe { - self.device.vk().destroy_semaphore(semaphore, None) - } - } - - /// Destroys a set of temporary objects. This is used if an error is encountered during the - /// build process. - fn destroy_temporary_objects(&self, objects: &mut [ObjectCreateMetadata]) { - // Iterate in reverse order to respect dependencies - for object in objects.iter_mut().rev() { - match object { - ObjectCreateMetadata::Buffer(BufferCreateMetadata{ handle, allocation, .. }) => { - if *handle != vk::Buffer::null() { - unsafe { self.device.vk().destroy_buffer(*handle, None) } - } - allocation.take().map(|alloc| self.allocator.free(alloc)); - }, - ObjectCreateMetadata::BufferView(BufferViewCreateMetadata{ handle, .. }) => { - if *handle != vk::BufferView::null() { - unsafe { self.device.vk().destroy_buffer_view(*handle, None) } - } - }, - ObjectCreateMetadata::Image(ImageCreateMetadata{ handle, allocation, .. }) => { - if *handle != vk::Image::null() { - unsafe { self.device.vk().destroy_image(*handle, None) } - } - allocation.take().map(|alloc| self.allocator.free(alloc)); - }, - ObjectCreateMetadata::ImageView(ImageViewCreateMetadata{ handle, .. }) => { - if *handle != vk::ImageView::null() { - unsafe { self.device.vk().destroy_image_view(*handle, None) } - } - } - } - } - } - - fn create_buffer(&self, meta: &mut BufferCreateMetadata) -> Result<(), ObjectCreateError> { - if meta.handle == vk::Buffer::null() { - let create_info = vk::BufferCreateInfo::builder() - .size(meta.desc.description.size) - .usage(meta.desc.description.usage_flags) - .sharing_mode(vk::SharingMode::EXCLUSIVE); - - meta.handle = unsafe { - self.device.vk().create_buffer(&create_info.build(), None) - }?; - } - if meta.allocation.is_none() { - meta.allocation = Some(self.allocator.allocate_buffer_memory(meta.handle, &meta.desc.strategy)?); - let alloc = meta.allocation.as_ref().unwrap(); - - unsafe { - self.device.vk().bind_buffer_memory(meta.handle, alloc.memory(), alloc.offset()) - }?; - } - Ok(()) - } - - fn create_buffer_view(&self, meta: &mut BufferViewCreateMetadata, split: &Splitter) -> Result<(), ObjectCreateError> { - if meta.handle == vk::BufferView::null() { - let buffer = match meta.desc.owning_set.as_ref() { - Some(set) => { - set.get_buffer_handle(meta.desc.buffer_id).ok_or(ObjectCreateError::InvalidReference)? - } - None => { - let index = meta.desc.buffer_id.get_index() as usize; - match split.get(index).ok_or(ObjectCreateError::InvalidReference)? { - ObjectCreateMetadata::Buffer(BufferCreateMetadata{ handle, .. }) => *handle, - _ => return Err(ObjectCreateError::InvalidReference) - } - } - }; - - let create_info = vk::BufferViewCreateInfo::builder() - .buffer(buffer) - .format(meta.desc.description.format.get_format()) - .offset(meta.desc.description.range.offset) - .range(meta.desc.description.range.length); - - meta.handle = unsafe { - self.device.vk().create_buffer_view(&create_info.build(), None)? - } - } - Ok(()) - } - - fn create_image(&self, meta: &mut ImageCreateMetadata) -> Result<(), ObjectCreateError> { - if meta.handle == vk::Image::null() { - let create_info = vk::ImageCreateInfo::builder() - .image_type(meta.desc.description.spec.size.get_vulkan_type()) - .format(meta.desc.description.spec.format.get_format()) - .extent(meta.desc.description.spec.size.as_extent_3d()) - .mip_levels(meta.desc.description.spec.size.get_mip_levels()) - .array_layers(meta.desc.description.spec.size.get_array_layers()) - .samples(meta.desc.description.spec.sample_count) - .tiling(vk::ImageTiling::OPTIMAL) // TODO we need some way to turn this linear - .usage(meta.desc.description.usage_flags) - .sharing_mode(vk::SharingMode::EXCLUSIVE); - - meta.handle = unsafe { - self.device.vk().create_image(&create_info.build(), None) - }?; - } - if meta.allocation.is_none() { - meta.allocation = Some(self.allocator.allocate_image_memory(meta.handle, &meta.desc.strategy)?); - let alloc = meta.allocation.as_ref().unwrap(); - - unsafe { - self.device.vk().bind_image_memory(meta.handle, alloc.memory(), alloc.offset()) - }?; - } - Ok(()) - } - - fn create_image_view(&self, meta: &mut ImageViewCreateMetadata, split: Splitter) -> Result<(), ObjectCreateError> { - if meta.handle == vk::ImageView::null() { - let image = match meta.desc.owning_set.as_ref() { - Some(set) => { - set.get_image_handle(meta.desc.image_id).ok_or(ObjectCreateError::InvalidReference)? - } - None => { - let index = meta.desc.image_id.get_index() as usize; - match split.get(index).ok_or(ObjectCreateError::InvalidReference)? { - ObjectCreateMetadata::Image(ImageCreateMetadata{ handle, .. }) => *handle, - _ => return Err(ObjectCreateError::InvalidReference) - } - } - }; - - let create_info = vk::ImageViewCreateInfo::builder() - .image(image) - .view_type(meta.desc.description.view_type) - .format(meta.desc.description.format.get_format()) - .components(meta.desc.description.components) - .subresource_range(meta.desc.description.subresource_range.as_vk_subresource_range()); - - meta.handle = unsafe { - self.device.vk().create_image_view(&create_info, None)? - } - } - Ok(()) - } - - /// Creates the objects for a temporary object data list - fn create_objects_for_metadata(&self, objects: &mut [ObjectCreateMetadata]) -> Result<(), ObjectCreateError> { - - // Since every entry can only reference previous entries its safe to iterate over them just once - for i in 0..objects.len() { - let (split, object) = Splitter::new(objects, i); - - match object { - ObjectCreateMetadata::Buffer(meta) => self.create_buffer(meta)?, - ObjectCreateMetadata::BufferView(meta) => self.create_buffer_view(meta, &split)?, - ObjectCreateMetadata::Image(meta) => self.create_image(meta)?, - ObjectCreateMetadata::ImageView(meta) => self.create_image_view(meta, split)?, - } - } - - Ok(()) - } - - /// Converts a object request description list to a temporary object data list - fn generate_objects_metadata<'a>(&self, objects: &'a [ObjectRequestDescription]) -> Vec> { - objects.iter().map(|request| { - match request { - ObjectRequestDescription::Buffer(desc) => { - ObjectCreateMetadata::make_buffer(desc) - } - ObjectRequestDescription::BufferView(desc) => { - ObjectCreateMetadata::make_buffer_view(desc) - } - ObjectRequestDescription::Image(desc) => { - ObjectCreateMetadata::make_image(desc) - } - ObjectRequestDescription::ImageView(desc) => { - ObjectCreateMetadata::make_image_view(desc) - } - } - }).collect() - } - - /// Converts a temporary object data list to a object data list and allocation meta instance - fn flatten_object_metadata(&self, objects: Vec) -> (Box<[ObjectData]>, Box<[Allocation]>) { - let mut allocations = Vec::new(); - let mut object_data = Vec::with_capacity(objects.len()); - - for object in objects.into_iter() { - object_data.push(match object { - ObjectCreateMetadata::Buffer(BufferCreateMetadata{ handle, allocation, .. }) => { - match allocation { - None => {} - Some(allocation) => allocations.push(allocation) - } - ObjectData::Buffer { handle } - } - ObjectCreateMetadata::BufferView(BufferViewCreateMetadata{ handle, desc, .. }) => { - ObjectData::BufferView { - handle, - source_set: desc.owning_set.clone(), - } - } - ObjectCreateMetadata::Image(ImageCreateMetadata{ handle, allocation, .. }) => { - match allocation { - None => {} - Some(allocation) => allocations.push(allocation) - } - ObjectData::Image { handle } - } - ObjectCreateMetadata::ImageView(ImageViewCreateMetadata{ handle, desc, .. }) => { - ObjectData::ImageView { - handle, - source_set: desc.owning_set.clone(), - } - } - }); - } - - (object_data.into_boxed_slice(), allocations.into_boxed_slice()) - } - - /// Creates objects for a object request description list - fn create_objects(&self, objects: &[ObjectRequestDescription]) -> (Box<[ObjectData]>, Box<[Allocation]>) { - let mut objects = self.generate_objects_metadata(objects); - self.create_objects_for_metadata(objects.as_mut_slice()).map_err(|err| { - self.destroy_temporary_objects(objects.as_mut_slice()); err - }).unwrap(); - - self.flatten_object_metadata(objects) - } - - /// Destroys objects previously created using [`ObjectManagerImpl::create_objects`] - fn destroy_objects(&self, objects: &[ObjectData], allocations: Box<[Allocation]>) { - for object in objects { - match object { - ObjectData::BufferView { handle, .. } => { - unsafe{ self.device.vk().destroy_buffer_view(*handle, None) } - } - ObjectData::ImageView { handle, .. } => { - unsafe{ self.device.vk().destroy_image_view(*handle, None) } - } - _ => {} - } - } - for object in objects { - match object { - ObjectData::Buffer { handle, .. } => { - unsafe{ self.device.vk().destroy_buffer(*handle, None) } - } - ObjectData::Image { handle, .. } => { - unsafe{ self.device.vk().destroy_image(*handle, None) } - } - _ => {} - } - } - - for allocation in allocations.into_vec() { - self.allocator.free(allocation); - } - } -} - -/// Public object manager api. -/// -/// This is a smart pointer reference to an internal struct. -pub struct ObjectManager(Arc); - -impl ObjectManager { - /// Creates a new ObjectManager - pub fn new(device: crate::rosella::DeviceContext) -> Self { - Self(Arc::new(ObjectManagerImpl::new(device))) - } - - /// Creates a new synchronization group managed by this object manager - pub fn create_synchronization_group(&self) -> SynchronizationGroup { - SynchronizationGroup::new(self.clone(), self.0.create_timeline_semaphore(0u64)) - } - - /// Creates a new object set builder - pub fn create_object_set(&self, synchronization_group: SynchronizationGroup) -> ObjectSetBuilder { - // if synchronization_group.get_manager() != self { - // panic!("Synchronization group is not owned by manager") - // } TODO fix pointer equality - - ObjectSetBuilder::new(synchronization_group) - } - - /// Creates a new object set builder without a synchronization group - pub fn create_no_group_object_set(&self) -> ObjectSetBuilder { - ObjectSetBuilder::new_no_group(self.clone()) - } - - // Internal function that destroys a semaphore created for a synchronization group - fn destroy_semaphore(&self, semaphore: vk::Semaphore) { - self.0.destroy_semaphore(semaphore) - } - - fn create_objects(&self, objects: &[ObjectRequestDescription]) -> (Box<[ObjectData]>, Box<[Allocation]>) { - self.0.create_objects(objects) - } - - // Internal function that destroys objects and allocations created for a object set - fn destroy_objects(&self, objects: Box<[ObjectData]>, allocations: Box<[Allocation]>) { - self.0.destroy_objects(&objects, allocations) - } -} - -impl Clone for ObjectManager { - fn clone(&self) -> Self { - Self( self.0.clone() ) - } -} - -#[cfg(test)] -mod tests { - use crate::objects::{BufferRange, ImageSize, ImageSpec}; - use crate::objects::buffer::{BufferCreateDesc, BufferViewCreateDesc}; - use crate::objects::image::ImageCreateDesc; - use super::*; - - fn create() -> ObjectManager { - let (_, device) = crate::test::make_headless_instance_device(); - ObjectManager::new(device) - } - - #[test] - fn create_destroy() { - let (_, device) = crate::test::make_headless_instance_device(); - let manager = ObjectManager::new(device); - drop(manager); - } - - #[test] - fn create_synchronization_group() { - let manager = create(); - let group = manager.create_synchronization_group(); - let group2 = manager.create_synchronization_group(); - - assert_eq!(group, group); - assert_eq!(group2, group2); - assert_ne!(group, group2); - - drop(group2); - drop(group); - } - - #[test] - fn create_object_set_buffer() { - let manager = create(); - let group = manager.create_synchronization_group(); - - let mut builder = manager.create_object_set(group.clone()); - let desc = BufferCreateDesc::new_simple(1024, vk::BufferUsageFlags::TRANSFER_SRC | vk::BufferUsageFlags::TRANSFER_DST); - let id = builder.add_default_gpu_only_buffer(desc); - - let set = builder.build(); - - assert_eq!(set.get_synchronization_group(), Some(&group)); - - assert!(set.get_buffer_handle(id).is_some()); - - drop(set); - } - - #[test] - fn create_object_set_image() { - let manager = create(); - let group = manager.create_synchronization_group(); - - let mut builder = manager.create_object_set(group.clone()); - let desc = ImageCreateDesc::new_simple(ImageSpec::new_single_sample(ImageSize::make_1d(32), &crate::objects::Format::R16_UNORM), - vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::TRANSFER_DST); - let id = builder.add_default_gpu_only_image(desc); - - let set = builder.build(); - - assert_eq!(set.get_synchronization_group(), Some(&group)); - - assert!(set.get_image_handle(id).is_some()); - - drop(set); - } - - #[test] - fn create_object_set_buffer_view() { - let manager = create(); - let group = manager.create_synchronization_group(); - - let mut builder = manager.create_object_set(group.clone()); - let buffer_desc = BufferCreateDesc::new_simple( - 1024, - vk::BufferUsageFlags::TRANSFER_SRC | vk::BufferUsageFlags::UNIFORM_TEXEL_BUFFER); - let buffer_id = builder.add_default_gpu_only_buffer(buffer_desc); - let view_desc = BufferViewCreateDesc::new_simple(BufferRange { offset: 256, length: 256 }, &crate::objects::Format::R16_UNORM); - let view_id = builder.add_internal_buffer_view(view_desc, buffer_id); - - let set = builder.build(); - - assert!(set.get_buffer_handle(buffer_id).is_some()); - assert!(set.get_buffer_view_handle(view_id).is_some()); - - let mut builder = manager.create_object_set(group.clone()); - let view_desc = BufferViewCreateDesc::new_simple(BufferRange { offset: 256, length: 256 }, &crate::objects::Format::R16_UNORM); - let view2_id = builder.add_external_buffer_view(view_desc, set.clone(), buffer_id); - - let set2 = builder.build(); - - assert!(set2.get_buffer_view_handle(view2_id).is_some()); - - // Test that original set does not get destroyed early - drop(set); - drop(set2); - } -} - -struct BufferRequestDescription { - pub description: BufferCreateDesc, - pub strategy: AllocationStrategy, -} - -struct BufferViewRequestDescription { - pub description: BufferViewCreateDesc, - /// The set that owns the source buffer of the view. If None the source buffer must be part of - /// the same set of requests as this request. - pub owning_set: Option, - pub buffer_id: id::BufferId, -} - -struct ImageRequestDescription { - pub description: ImageCreateDesc, - pub strategy: AllocationStrategy, -} - -struct ImageViewRequestDescription { - pub description: ImageViewCreateDesc, - /// The set that owns the source image of the view. If None the source image must be part of - /// the same set of requests as this request. - pub owning_set: Option, - pub image_id: id::ImageId, -} - -/// Describes a single object request -enum ObjectRequestDescription { - Buffer(BufferRequestDescription), - BufferView(BufferViewRequestDescription), - Image(ImageRequestDescription), - ImageView(ImageViewRequestDescription), -} - -impl ObjectRequestDescription { - pub fn make_buffer(description: BufferCreateDesc, strategy: AllocationStrategy) -> Self { - ObjectRequestDescription::Buffer(BufferRequestDescription{ - description, - strategy - }) - } - - pub fn make_buffer_view(description: BufferViewCreateDesc, owning_set: Option, buffer_id: id::BufferId) -> Self { - ObjectRequestDescription::BufferView(BufferViewRequestDescription{ - description, - owning_set, - buffer_id - }) - } - - pub fn make_image(description: ImageCreateDesc, strategy: AllocationStrategy) -> Self { - ObjectRequestDescription::Image(ImageRequestDescription{ - description, - strategy - }) - } - - pub fn make_image_view(description: ImageViewCreateDesc, owning_set: Option, image_id: id::ImageId) -> Self { - ObjectRequestDescription::ImageView(ImageViewRequestDescription{ - description, - owning_set, - image_id - }) - } -} diff --git a/src/objects/manager/object_set.rs b/src/objects/manager/object_set.rs deleted file mode 100644 index d977252..0000000 --- a/src/objects/manager/object_set.rs +++ /dev/null @@ -1,438 +0,0 @@ -use std::cmp::Ordering; -use std::hash::{Hash, Hasher}; -use std::mem::ManuallyDrop; -use std::sync::Arc; -use crate::objects::buffer::{BufferCreateDesc, BufferViewCreateDesc}; -use crate::objects::image::{ImageCreateDesc, ImageViewCreateDesc}; -use crate::objects::{id, ObjectManager}; -use crate::objects::manager::synchronization_group::SynchronizationGroup; -use crate::util::id::GlobalId; - -use ash::vk; -use ash::vk::Handle; -use crate::objects::manager::allocator::{Allocation, AllocationStrategy}; -use crate::objects::manager::ObjectRequestDescription; - -pub(super) enum ObjectData { - Buffer{ - handle: vk::Buffer, - }, - BufferView{ - handle: vk::BufferView, - #[allow(unused)] // This is needed to prevent the source set from being destroyed early - source_set: Option, - }, - Image { - handle: vk::Image, - }, - ImageView { - handle: vk::ImageView, - #[allow(unused)] // This is needed to prevent the source set from being destroyed early - source_set: Option, - } -} - -impl ObjectData { - fn get_raw_handle(&self) -> u64 { - match self { - ObjectData::Buffer { handle, .. } => handle.as_raw(), - ObjectData::BufferView {handle, .. } => handle.as_raw(), - ObjectData::Image { handle, .. } => handle.as_raw(), - ObjectData::ImageView { handle, .. } => handle.as_raw(), - } - } -} - -pub(super) struct ObjectSetData { - pub objects: Box<[ObjectData]>, - pub allocations: Box<[Allocation]> -} - -/// Utility struct used to build an object set. -/// -/// Collects information about objects that need to be created for an object set. The objects are -/// only created once the build method is called. -pub struct ObjectSetBuilder { - synchronization_group: Option, - manager: ObjectManager, - set_id: GlobalId, - requests: Vec, - requires_group: bool, -} - -impl ObjectSetBuilder { - pub(super) fn new(synchronization_group: SynchronizationGroup) -> Self { - let manager = synchronization_group.get_manager().clone(); - Self { - synchronization_group: Some(synchronization_group), - manager, - set_id: GlobalId::new(), - requests: Vec::new(), - requires_group: false, - } - } - - pub(super) fn new_no_group(manager: ObjectManager) -> Self { - Self { - synchronization_group: None, - manager, - set_id: GlobalId::new(), - requests: Vec::new(), - requires_group: false, - } - } - - /// Adds a request for a buffer that only needs to be accessed by the gpu - pub fn add_default_gpu_only_buffer(&mut self, desc: BufferCreateDesc) -> id::BufferId { - if self.synchronization_group.is_none() { - panic!("Attempted to add buffer to object set without synchronization group"); - } - self.requires_group = true; - - let index = self.requests.len(); - - self.requests.push(ObjectRequestDescription::make_buffer(desc, AllocationStrategy::AutoGpuOnly)); - - id::BufferId::new(self.set_id, index as u64) - } - - /// Adds a request for a buffer that needs to be accessed by both gpu and cpu - pub fn add_default_gpu_cpu_buffer(&mut self, desc: BufferCreateDesc) -> id::BufferId { - if self.synchronization_group.is_none() { - panic!("Attempted to add buffer to object set without synchronization group"); - } - self.requires_group = true; - - let index = self.requests.len(); - - self.requests.push(ObjectRequestDescription::make_buffer(desc, AllocationStrategy::AutoGpuCpu)); - - id::BufferId::new(self.set_id, index as u64) - } - - /// Adds a buffer view for a buffer created as part of this object set - pub fn add_internal_buffer_view(&mut self, desc: BufferViewCreateDesc, buffer: id::BufferId) -> id::BufferViewId { - if self.synchronization_group.is_none() { - panic!("Attempted to add buffer view to object set without synchronization group"); - } - self.requires_group = true; - - if buffer.get_global_id() != self.set_id { - panic!("Buffer global id does not match set id") - } - let index = self.requests.len(); - - self.requests.push(ObjectRequestDescription::make_buffer_view(desc, None, buffer)); - - id::BufferViewId::new(self.set_id, index as u64) - } - - /// Adds a buffer view for a buffer owned by a different object set - pub fn add_external_buffer_view(&mut self, desc: BufferViewCreateDesc, set: ObjectSet, buffer: id::BufferId) -> id::BufferViewId { - if self.synchronization_group.is_none() { - panic!("Attempted to add buffer view to object set without synchronization group"); - } - self.requires_group = true; - - if buffer.get_global_id() != set.get_set_id() { - panic!("Buffer global id does not match set id") - } - - if set.get_synchronization_group().unwrap() != self.synchronization_group.as_ref().unwrap() { - panic!("Buffer does not match internal synchronization group") - } - - let index = self.requests.len(); - - self.requests.push(ObjectRequestDescription::make_buffer_view(desc, Some(set), buffer)); - - id::BufferViewId::new(self.set_id, index as u64) - } - - /// Adds a request for a image that only needs to be accessed by the gpu - pub fn add_default_gpu_only_image(&mut self, desc: ImageCreateDesc) -> id::ImageId { - if self.synchronization_group.is_none() { - panic!("Attempted to add image to object set without synchronization group"); - } - self.requires_group = true; - - let index = self.requests.len(); - - self.requests.push(ObjectRequestDescription::make_image(desc, AllocationStrategy::AutoGpuOnly)); - - id::ImageId::new(self.set_id, index as u64) - } - - /// Adds a request for a image that needs to be accessed by both gpu and cpu - pub fn add_default_gpu_cpu_image(&mut self, desc: ImageCreateDesc) -> id::ImageId { - if self.synchronization_group.is_none() { - panic!("Attempted to add image to object set without synchronization group"); - } - self.requires_group = true; - - let index = self.requests.len(); - - self.requests.push(ObjectRequestDescription::make_image(desc, AllocationStrategy::AutoGpuCpu)); - - id::ImageId::new(self.set_id, index as u64) - } - - /// Adds a image view for a image created as part of this object set - pub fn add_internal_image_view(&mut self, desc: ImageViewCreateDesc, image: id::ImageId) -> id::ImageViewId { - if self.synchronization_group.is_none() { - panic!("Attempted to add image view to object set without synchronization group"); - } - self.requires_group = true; - - if image.get_global_id() != self.set_id { - panic!("Image global id does not match set id") - } - let index = self.requests.len(); - - self.requests.push(ObjectRequestDescription::make_image_view(desc, None, image)); - - id::ImageViewId::new(self.set_id, index as u64) - } - - /// Adds a image view for a image owned by a different object set - pub fn add_external_image_view(&mut self, desc: ImageViewCreateDesc, set: ObjectSet, image: id::ImageId) -> id::ImageViewId { - if self.synchronization_group.is_none() { - panic!("Attempted to add image view to object set without synchronization group"); - } - self.requires_group = true; - - if image.get_global_id() != set.get_set_id() { - panic!("Image global id does not match set id") - } - - if set.get_synchronization_group().unwrap() != self.synchronization_group.as_ref().unwrap() { - panic!("Image does not match internal synchronization group") - } - - let index = self.requests.len(); - - self.requests.push(ObjectRequestDescription::make_image_view(desc, Some(set), image)); - - id::ImageViewId::new(self.set_id, index as u64) - } - - /// Creates the objects and returns the resulting object set - pub fn build(self) -> ObjectSet { - let group = if self.requires_group { self.synchronization_group } else { None }; - - let (objects, allocation) = self.manager.create_objects(self.requests.as_slice()); - ObjectSet::new(self.set_id, group, self.manager, objects, allocation) - } -} - -// Internal implementation of the object set -struct ObjectSetImpl { - group: Option, - manager: ObjectManager, - set_id: GlobalId, - - // Screw unwrap - data: ManuallyDrop, -} - -impl ObjectSetImpl { - fn new(set_id: GlobalId, synchronization_group: Option, manager: ObjectManager, objects: Box<[ObjectData]>, allocations: Box<[Allocation]>) -> Self { - Self{ - group: synchronization_group, - manager, - set_id, - data: ManuallyDrop::new(ObjectSetData { - objects, - allocations, - }) - } - } - - fn get_raw_handle(&self, id: id::GenericId) -> Option { - if id.get_global_id() != self.set_id { - return None; - } - - // Invalid local id but matching global is a serious error - Some(self.data.objects.get(id.get_index() as usize).unwrap().get_raw_handle()) - } - - fn get_buffer_handle(&self, id: id::BufferId) -> Option { - if id.get_global_id() != self.set_id { - return None; - } - - // Invalid local id but matching global is a serious error - match self.data.objects.get(id.get_index() as usize).unwrap() { - ObjectData::Buffer { handle, .. } => Some(*handle), - _ => panic!("Object type mismatch"), - } - } - - fn get_buffer_view_handle(&self, id: id::BufferViewId) -> Option { - if id.get_global_id()!= self.set_id { - return None; - } - - // Invalid local id but matching global is a serious error - match self.data.objects.get(id.get_index() as usize).unwrap() { - ObjectData::BufferView { handle, .. } => Some(*handle), - _ => panic!("Object type mismatch"), - } - } - - fn get_image_handle(&self, id: id::ImageId) -> Option { - if id.get_global_id() != self.set_id { - return None; - } - - // Invalid local id but matching global is a serious error - match self.data.objects.get(id.get_index() as usize).unwrap() { - ObjectData::Image { handle, .. } => Some(*handle), - _ => panic!("Object type mismatch"), - } - } - - fn get_image_view_handle(&self, id: id::ImageViewId) -> Option { - if id.get_global_id()!= self.set_id { - return None; - } - - // Invalid local id but matching global is a serious error - match self.data.objects.get(id.get_index() as usize).unwrap() { - ObjectData::ImageView { handle, .. } => Some(*handle), - _ => panic!("Object type mismatch"), - } - } -} - -impl Drop for ObjectSetImpl { - fn drop(&mut self) { - let data = unsafe { ManuallyDrop::take(&mut self.data) }; - self.manager.destroy_objects(data.objects, data.allocations); - } -} - -// Needed because the SynchronizationSet mutex also protects the ObjectSet -unsafe impl Sync for ObjectSetImpl { -} - -impl PartialEq for ObjectSetImpl { - fn eq(&self, other: &Self) -> bool { - self.set_id.eq(&other.set_id) - } -} - -impl Eq for ObjectSetImpl { -} - -impl PartialOrd for ObjectSetImpl { - fn partial_cmp(&self, other: &Self) -> Option { - self.set_id.partial_cmp(&other.set_id) - } -} - -impl Ord for ObjectSetImpl { - fn cmp(&self, other: &Self) -> Ordering { - self.set_id.cmp(&other.set_id) - } -} - - -/// Public object set api. -/// -/// This is a smart pointer reference to an internal struct. -pub struct ObjectSet(Arc); - -impl ObjectSet { - fn new(set_id: GlobalId, synchronization_group: Option, manager: ObjectManager, objects: Box<[ObjectData]>, allocations: Box<[Allocation]>) -> Self { - Self(Arc::new(ObjectSetImpl::new(set_id, synchronization_group, manager, objects, allocations))) - } - - pub fn get_set_id(&self) -> GlobalId { - self.0.set_id - } - - /// Returns the synchronization group that controls access to this object set. - pub fn get_synchronization_group(&self) -> Option<&SynchronizationGroup> { - self.0.group.as_ref() - } - - /// Returns the handle of an object that is part of this object set. - /// - /// If the id is not part of the object set (i.e. the global id does not match) None will be - /// returned. If the id is invalid (matching global id but local id is invalid) the function - /// panics. - pub fn get_raw_handle(&self, id: id::GenericId) -> Option { - self.0.get_raw_handle(id) - } - - /// Returns the handle of a buffer that is part of this object set. - /// - /// If the id is not part of the object set (i.e. the global id does not match) None will be - /// returned. If the id is invalid (matching global id but local id is invalid or object type - /// is not a buffer) the function panics. - pub fn get_buffer_handle(&self, id: id::BufferId) -> Option { - self.0.get_buffer_handle(id) - } - - /// Returns the handle of a buffer view that is part of this object set. - /// - /// If the id is not part of the object set (i.e. the global id does not match) None will be - /// returned. If the id is invalid (matching global id but local id is invalid or object type - /// is not a buffer view) the function panics. - pub fn get_buffer_view_handle(&self, id: id::BufferViewId) -> Option { - self.0.get_buffer_view_handle(id) - } - - /// Returns the handle of a image that is part of this object set. - /// - /// If the id is not part of the object set (i.e. the global id does not match) None will be - /// returned. If the id is invalid (matching global id but local id is invalid or object type - /// is not a image) the function panics. - pub fn get_image_handle(&self, id: id::ImageId) -> Option { - self.0.get_image_handle(id) - } - - /// Returns the handle of a image view that is part of this object set. - /// - /// If the id is not part of the object set (i.e. the global id does not match) None will be - /// returned. If the id is invalid (matching global id but local id is invalid or object type - /// is not a image view) the function panics. - pub fn get_image_view_handle(&self, id: id::ImageViewId) -> Option { - self.0.get_image_view_handle(id) - } -} - -impl Clone for ObjectSet { - fn clone(&self) -> Self { - Self( self.0.clone() ) - } -} - -impl PartialEq for ObjectSet { - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } -} - -impl Eq for ObjectSet { -} - -impl PartialOrd for ObjectSet { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for ObjectSet { - fn cmp(&self, other: &Self) -> Ordering { - self.0.cmp(&other.0) - } -} - -impl Hash for ObjectSet { - fn hash(&self, state: &mut H) { - self.0.set_id.hash(state) - } -} \ No newline at end of file diff --git a/src/objects/mod.rs b/src/objects/mod.rs index ee4c73d..3d3e391 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -2,20 +2,31 @@ pub mod format; pub mod image; pub mod buffer; pub mod id; -pub mod manager; pub mod swapchain; +pub mod surface; +pub mod allocator; +pub mod object_set; +pub mod synchronization_group; +pub mod resource_object_set; +pub mod swapchain_object_set; pub use format::Format; +pub use buffer::BufferSpec; +pub use buffer::BufferRange; +pub use buffer::BufferDescription; +pub use buffer::BufferViewDescription; + pub use image::ImageSize; pub use image::ImageSpec; pub use image::ImageSubresourceRange; +pub use image::ImageDescription; +pub use image::ImageViewDescription; -pub use buffer::BufferSpec; -pub use buffer::BufferRange; +pub use synchronization_group::SynchronizationGroup; +pub use synchronization_group::SynchronizationGroupSet; + +pub use object_set::ObjectSet; -pub use manager::ObjectManager; -pub use manager::synchronization_group::SynchronizationGroup; -pub use manager::synchronization_group::SynchronizationGroupSet; -pub use manager::object_set::ObjectSet; -pub use manager::object_set::ObjectSetBuilder; \ No newline at end of file +pub use resource_object_set::ResourceObjectSetBuilder; +pub use swapchain_object_set::SwapchainObjectSetBuilder; \ No newline at end of file diff --git a/src/objects/object_set.rs b/src/objects/object_set.rs new file mode 100644 index 0000000..5c2f8e9 --- /dev/null +++ b/src/objects/object_set.rs @@ -0,0 +1,152 @@ +use std::any::Any; +use std::cmp::Ordering; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; +use std::sync::Arc; +use ash::vk; + +use crate::objects::buffer::{BufferInfo, BufferViewInfo}; +use crate::objects::id; +use crate::objects::id::ObjectSetId; +use crate::objects::image::{ImageInfo, ImageViewInfo}; + +/// A trait that must be implemented by any object set implementation. +pub trait ObjectSetProvider { + /// Returns the id of this object set. + fn get_id(&self) -> ObjectSetId; + + /// Returns the handle of a buffer that is part of this object set. + /// + /// #Panics + /// If the buffer id does not belong to this object set or does not map to a buffer object. + unsafe fn get_buffer_handle(&self, _: id::BufferId) -> vk::Buffer { + panic!("ObjectSet does not support buffers"); + } + + /// Returns the [`BufferInfo`] struct for a buffer that is part of this object set. + /// + /// #Panics + /// If the buffer id does not belong to this object set or does not map to a buffer object. + fn get_buffer_info(&self, _: id::BufferId) -> &Arc { + panic!("ObjectSet does not support buffers"); + } + + /// Returns the handle of a buffer view that is part of this object set. + /// + /// #Panics + /// If the buffer view id does not belong to this object set or does not map to a buffer view + /// object. + unsafe fn get_buffer_view_handle(&self, _: id::BufferViewId) -> vk::BufferView { + panic!("ObjectSet does not support buffer views"); + } + + /// Returns the [`BufferViewInfo`] struct for a buffer view that is part of this object set. + /// + /// #Panics + /// If the buffer view id does not belong to this object set or does not map to a buffer view + /// object. + fn get_buffer_view_info(&self, _: id::BufferViewId) -> &BufferViewInfo { + panic!("ObjectSet does not support buffer views"); + } + + /// Returns the handle of a image that is part of this object set. + /// + /// #Panics + /// If the image id does not belong to this object set or does not map to a image object. + unsafe fn get_image_handle(&self, _: id::ImageId) -> vk::Image { + panic!("ObjectSet does not support images"); + } + + /// Returns the [`ImageInfo`] struct for a image that is part of this object set. + /// + /// #Panics + /// If the image id does not belong to this object set or does not map to a image object. + fn get_image_info(&self, _: id::ImageId) -> &Arc { + panic!("ObjectSet does not support images"); + } + + /// Returns the handle of a image view that is part of this object set. + /// + /// #Panics + /// If the image view id does not belong to this object set or does not map to a image view + /// object. + unsafe fn get_image_view_handle(&self, _: id::ImageViewId) -> vk::ImageView { + panic!("ObjectSet does not support image views"); + } + + /// Returns the [`ImageViewInfo`] struct for a image view that is part of this object set. + /// + /// #Panics + /// If the image view id does not belong to this object set or does not map to a image view + /// object. + fn get_image_view_info(&self, _: id::ImageViewId) -> &ImageViewInfo { + panic!("ObjectSet does not support image views"); + } + + /// Returns the handle of a swapchain that is part of this object set. + /// + /// #Panics + /// If the swapchain id does not belong to this object set or does not map to a swapchain + /// object. + unsafe fn get_swapchain_handle(&self, _: id::SwapchainId) -> vk::SwapchainKHR { + panic!("ObjectSet does not support swapchains"); + } + + unsafe fn get_semaphore_handle(&self, _: id::SemaphoreId) -> vk::Semaphore { + panic!("ObjectSet does not support semaphores"); + } + + unsafe fn get_fence_handle(&self, _: id::FenceId) -> vk::Fence { + panic!("ObjectSet does not support fences"); + } + + fn as_any(&self) -> &dyn Any; +} + +/// A wrapper type around the [`ObjectSetProvider`] trait. +/// +/// Provides a universal object set api. +#[derive(Clone)] +pub struct ObjectSet(Arc); + +impl ObjectSet { + /// Creates a new object set from the specified provider. + pub fn new(set: T) -> Self { + Self(Arc::new(set)) + } +} + +impl Deref for ObjectSet { + type Target = dyn ObjectSetProvider; + + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } +} + +impl PartialEq for ObjectSet { + fn eq(&self, other: &Self) -> bool { + self.0.get_id().eq(&other.0.get_id()) + } +} + +impl Eq for ObjectSet { +} + +impl PartialOrd for ObjectSet { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.get_id().partial_cmp(&other.0.get_id()) + } +} + +impl Ord for ObjectSet { + fn cmp(&self, other: &Self) -> Ordering { + self.0.get_id().cmp(&other.0.get_id()) + } +} + +impl Hash for ObjectSet { + fn hash(&self, state: &mut H) { + self.0.get_id().hash(state) + } +} \ No newline at end of file diff --git a/src/objects/resource_object_set.rs b/src/objects/resource_object_set.rs new file mode 100644 index 0000000..fd6ad4c --- /dev/null +++ b/src/objects/resource_object_set.rs @@ -0,0 +1,973 @@ +use std::any::Any; +use std::sync::Arc; +use ash::vk; +use crate::device::DeviceContext; + +use crate::objects::{id, ObjectSet, SynchronizationGroup}; +use crate::objects::buffer::{BufferDescription, BufferInfo, BufferViewDescription, BufferViewInfo}; +use crate::objects::id::{BufferId, BufferViewId, ImageId, ImageViewId, ObjectSetId}; +use crate::objects::image::{ImageDescription, ImageInfo, ImageViewDescription, ImageViewInfo}; +use crate::objects::allocator::{Allocation, AllocationError, AllocationStrategy}; +use crate::objects::object_set::ObjectSetProvider; +use crate::util::slice_splitter::Splitter; + +#[derive(Debug)] +pub enum ObjectCreateError { + Vulkan(vk::Result), + Allocation(AllocationError), + InvalidReference, +} + +impl<'s> From for ObjectCreateError { + fn from(err: vk::Result) -> Self { + ObjectCreateError::Vulkan(err) + } +} + +impl<'s> From for ObjectCreateError { + fn from(err: AllocationError) -> Self { + ObjectCreateError::Allocation(err) + } +} + +/// Resource object sets are object sets specifically designed for resources that require backing +/// memory and synchronization. (i.e. Buffers, BufferViews etc.) +/// +/// All objects of a resource object set have the same synchronization group. +/// +/// All of the objects are only created when then [`ResourceObjectSetBuilder::build`] function is +/// called. +/// +/// # Examples +/// +/// ``` +/// # use rosella_rs::objects::buffer::BufferDescription; +/// # use rosella_rs::objects::image::{ImageDescription, ImageViewDescription}; +/// # use rosella_rs::objects::resource_object_set::ResourceObjectSetBuilder; +/// # use rosella_rs::objects::{Format, ImageSize, ImageSpec, SynchronizationGroup}; +/// # let (_, device) = rosella_rs::test::make_headless_instance_device(); +/// use ash::vk; +/// +/// // We need a synchronization group for our objects +/// let synchronization_group = SynchronizationGroup::new(device); +/// +/// // Create a builder. It will use the synchronization group for all objects +/// let mut builder = ResourceObjectSetBuilder::new(synchronization_group); +/// +/// // Add a request for a device only buffer. The buffer wont be created yet. +/// let buffer_id = builder.add_default_gpu_only_buffer( +/// BufferDescription::new_simple(1024, vk::BufferUsageFlags::VERTEX_BUFFER) +/// ); +/// +/// // Add a request for a device only image. Again the image wont be created just yet. +/// let image_id = builder.add_default_gpu_only_image( +/// ImageDescription::new_simple( +/// ImageSpec::new_single_sample(ImageSize::make_2d(128, 128), &Format::R8G8B8A8_SRGB), +/// vk::ImageUsageFlags::SAMPLED, +/// ) +/// ); +/// +/// // We can add a image view for a previously requested image. +/// let image_view_id = builder.add_internal_image_view( +/// ImageViewDescription::make_full( +/// vk::ImageViewType::TYPE_2D, +/// &Format::R8G8B8A8_SRGB, +/// vk::ImageAspectFlags::COLOR +/// ), +/// image_id +/// ); +/// +/// // During the build call all of the objects will be created +/// let object_set = builder.build().unwrap(); +/// +/// // Now we can access the objects +/// let image_handle = unsafe { object_set.get_image_handle(image_id) }; +/// +/// // Or query information about them +/// let buffer_size = object_set.get_buffer_info(buffer_id).get_description().size; +/// +/// // The objects will be destroyed when the object set is dropped. The object set type uses Arc +/// // internally so it can be cloned and the objects will only be dropped when all references +/// // have been dropped. +/// ``` +pub struct ResourceObjectSetBuilder { + set_id: ObjectSetId, + device: DeviceContext, + synchronization_group: SynchronizationGroup, + requests: Vec, +} + +impl ResourceObjectSetBuilder { + /// Creates a new builder using the specified synchronization group + /// + /// The object set will use the device used for the synchronization group. + pub fn new(synchronization_group: SynchronizationGroup) -> Self { + let device = synchronization_group.get_device().clone(); + Self { + synchronization_group, + device, + set_id: ObjectSetId::new(), + requests: Vec::new(), + } + } + + /// Returns the index of the next object. + /// + /// #Panics + /// If the next index does not fit into a u16 number. + fn get_next_index(&self) -> u16 { + let index = self.requests.len(); + if index > u16::MAX as usize { + panic!("Too many objects"); + } + index as u16 + } + + /// Adds a request for a buffer that only needs to be accessed by the gpu. + /// + /// #Panics + /// If there are more requests than the max object set size. + pub fn add_default_gpu_only_buffer(&mut self, desc: BufferDescription) -> id::BufferId { + let index = self.get_next_index(); + self.requests.push(ResourceObjectCreateMetadata::make_buffer(desc, AllocationStrategy::AutoGpuOnly, self.synchronization_group.clone())); + + id::BufferId::new(self.set_id, index) + } + + /// Adds a request for a buffer that needs to be accessed by both the gpu and cpu. + /// + /// #Panics + /// If there are more requests than the max object set size. + pub fn add_default_gpu_cpu_buffer(&mut self, desc: BufferDescription) -> id::BufferId { + let index = self.get_next_index(); + + self.requests.push(ResourceObjectCreateMetadata::make_buffer(desc, AllocationStrategy::AutoGpuCpu, self.synchronization_group.clone())); + + id::BufferId::new(self.set_id, index) + } + + /// Adds a buffer view request for a buffer that is created as part of this object set. + /// + /// #Panics + /// If there are more requests than the max object set size or if the source buffer id does not + /// map to a buffer. + pub fn add_internal_buffer_view(&mut self, desc: BufferViewDescription, buffer: id::BufferId) -> id::BufferViewId { + if buffer.get_set_id() != self.set_id { + panic!("Buffer set id does not match builder set id"); + } + let info = match self.requests.get(buffer.get_index() as usize).unwrap() { + ResourceObjectCreateMetadata::Buffer(buff) => { + buff.info.clone() + } + _ => panic!("Buffer id does not map to a buffer") + }; + + let index = self.get_next_index(); + + self.requests.push(ResourceObjectCreateMetadata::make_buffer_view(desc, None, buffer, info)); + + id::BufferViewId::new(self.set_id, index) + } + + /// Adds a buffer view request for a buffer that is part of a different object set. + /// + /// #Panics + /// If there are more requests than the max object set size or if the source buffer id is + /// invalid. + pub fn add_external_buffer_view(&mut self, desc: BufferViewDescription, set: ObjectSet, buffer: id::BufferId) -> id::BufferViewId { + if buffer.get_set_id() != set.get_id() { + panic!("Buffer set id does not match object set id"); + } + let info = set.get_buffer_info(buffer).clone(); + + let index = self.get_next_index(); + + self.requests.push(ResourceObjectCreateMetadata::make_buffer_view(desc, Some(set), buffer, info)); + + id::BufferViewId::new(self.set_id, index) + } + + /// Adds a request for a image that only needs to be accessed by the gpu. + /// + /// #Panics + /// If there are more requests than the max object set size. + pub fn add_default_gpu_only_image(&mut self, desc: ImageDescription) -> id::ImageId { + let index = self.get_next_index(); + + self.requests.push(ResourceObjectCreateMetadata::make_image(desc, AllocationStrategy::AutoGpuOnly, self.synchronization_group.clone())); + + id::ImageId::new(self.set_id, index) + } + + /// Adds a request for a image that needs to be accessed by both the gpu and cpu. + /// + /// #Panics + /// If there are more requests than the max object set size. + pub fn add_default_gpu_cpu_image(&mut self, desc: ImageDescription) -> id::ImageId { + let index = self.get_next_index(); + + self.requests.push(ResourceObjectCreateMetadata::make_image(desc, AllocationStrategy::AutoGpuCpu, self.synchronization_group.clone())); + + id::ImageId::new(self.set_id, index) + } + + /// Adds a image view request for a image that is created as part of this object set. + /// + /// #Panics + /// If there are more requests than the max object set size or if the source image id is + /// invalid. + pub fn add_internal_image_view(&mut self, desc: ImageViewDescription, image: id::ImageId) -> id::ImageViewId { + if image.get_set_id() != self.set_id { + panic!("Image set id does not match builder set id"); + } + let info = match self.requests.get(image.get_index() as usize).unwrap() { + ResourceObjectCreateMetadata::Image(img) => { + img.info.clone() + } + _ => panic!("Image id does not map to a image") + }; + + let index = self.get_next_index(); + + self.requests.push(ResourceObjectCreateMetadata::make_image_view(desc, None, image, info)); + + id::ImageViewId::new(self.set_id, index) + } + + /// Adds a image view request for a image that is part of a different object set. + /// + /// #Panics + /// If there are more requests than the max object set size or if the source image id is + /// invalid. + pub fn add_external_image_view(&mut self, desc: ImageViewDescription, set: ObjectSet, image: id::ImageId) -> id::ImageViewId { + if image.get_set_id() != set.get_id() { + panic!("Buffer set id does not match object set id"); + } + let info = set.get_image_info(image).clone(); + + let index = self.get_next_index(); + + self.requests.push(ResourceObjectCreateMetadata::make_image_view(desc, Some(set), image, info)); + + id::ImageViewId::new(self.set_id, index) + } + + fn create_objects(&mut self) -> Result<(), ObjectCreateError> { + let slice = self.requests.as_mut_slice(); + + for i in 0..slice.len() { + let (splitter, elem) = Splitter::new(slice, i); + elem.create(&self.device, &splitter)?; + } + + Ok(()) + } + + fn destroy_objects(&mut self) { + for request in self.requests.iter_mut().rev() { + request.abort(&self.device) + } + } + + /// Creates all objects and returns the completed object set. + pub fn build(mut self) -> Result { + if let Err(error) = self.create_objects() { + self.destroy_objects(); + return Err(error) + } + + let mut allocations = Vec::new(); + let mut objects = Vec::with_capacity(self.requests.len()); + + for request in self.requests { + let (object, allocation) = request.reduce(); + objects.push(object); + + if let Some(allocation) = allocation { + allocations.push(allocation) + } + } + + Ok(ObjectSet::new(ResourceObjectSet { + set_id: self.set_id, + device: self.device, + objects: objects.into_boxed_slice(), + allocations: allocations.into_boxed_slice(), + })) + } +} + +struct BufferCreateMetadata { + info: Arc, + strategy: AllocationStrategy, + handle: vk::Buffer, + allocation: Option, +} + +impl BufferCreateMetadata { + fn new(desc: BufferDescription, strategy: AllocationStrategy, group: SynchronizationGroup) -> Self { + Self { + info: Arc::new(BufferInfo::new(desc, group)), + strategy, + handle: vk::Buffer::null(), + allocation: None, + } + } + + fn create(&mut self, device: &DeviceContext, _: &Splitter) -> Result<(), ObjectCreateError> { + if self.handle == vk::Buffer::null() { + let desc = self.info.get_description(); + let create_info = vk::BufferCreateInfo::builder() + .size(desc.size) + .usage(desc.usage_flags) + .sharing_mode(vk::SharingMode::EXCLUSIVE); + + self.handle = unsafe { + device.vk().create_buffer(&create_info, None) + }?; + } + if self.allocation.is_none() { + self.allocation = Some(device.get_allocator().allocate_buffer_memory(self.handle, &self.strategy)?); + let alloc = self.allocation.as_ref().unwrap(); + + unsafe { + device.vk().bind_buffer_memory(self.handle, alloc.memory(), alloc.offset()) + }?; + } + Ok(()) + } + + fn abort(&mut self, device: &DeviceContext) { + if self.handle != vk::Buffer::null() { + unsafe { device.vk().destroy_buffer(self.handle, None) } + self.handle = vk::Buffer::null(); + } + match self.allocation.take() { + Some(alloc) => { + device.get_allocator().free(alloc); + } + None => {} + } + } + + fn reduce(self) -> (ResourceObjectData, Option) { + if self.handle == vk::Buffer::null() || self.allocation.is_none() { + panic!("Incomplete Buffer object") + } + + let object = ResourceObjectData::Buffer { + handle: self.handle, + info: self.info, + }; + + (object , self.allocation) + } +} + +struct BufferViewCreateMetadata { + info: Box, + buffer_set: Option, + buffer_id: id::BufferId, + handle: vk::BufferView, +} + +impl BufferViewCreateMetadata { + fn new(desc: BufferViewDescription, buffer_set: Option, buffer_id: id::BufferId, buffer_info: Arc) -> Self { + Self { + info: Box::new(BufferViewInfo::new(desc, buffer_id, buffer_info)), + buffer_set, + buffer_id, + handle: vk::BufferView::null(), + } + } + + fn create(&mut self, device: &DeviceContext, split: &Splitter) -> Result<(), ObjectCreateError> { + if self.handle == vk::BufferView::null() { + let buffer = match self.buffer_set.as_ref() { + Some(set) => { + unsafe { set.get_buffer_handle(self.buffer_id) } + } + None => { + let index = self.buffer_id.get_index() as usize; + match split.get(index).unwrap() { + ResourceObjectCreateMetadata::Buffer(BufferCreateMetadata{ handle, .. }) => *handle, + _ => return Err(ObjectCreateError::InvalidReference) + } + } + }; + + let desc = self.info.get_description(); + let create_info = vk::BufferViewCreateInfo::builder() + .buffer(buffer) + .format(desc.format.get_format()) + .offset(desc.range.offset) + .range(desc.range.length); + + self.handle = unsafe { + device.vk().create_buffer_view(&create_info.build(), None)? + } + } + Ok(()) + } + + fn abort(&mut self, device: &DeviceContext) { + if self.handle != vk::BufferView::null() { + unsafe { device.vk().destroy_buffer_view(self.handle, None) } + self.handle = vk::BufferView::null() + } + } + + fn reduce(self) -> (ResourceObjectData, Option) { + if self.handle == vk::BufferView::null() { + panic!("Incomplete BufferView object") + } + + let object = ResourceObjectData::BufferView { + handle: self.handle, + info: self.info, + source_set: self.buffer_set, + }; + + (object, None) + } +} + +struct ImageCreateMetadata { + info: Arc, + strategy: AllocationStrategy, + handle: vk::Image, + allocation: Option, +} + +impl ImageCreateMetadata { + fn new(desc: ImageDescription, strategy: AllocationStrategy, group: SynchronizationGroup) -> Self { + Self { + info: Arc::new(ImageInfo::new(desc, group)), + strategy, + handle: vk::Image::null(), + allocation: None, + } + } + + fn create(&mut self, device: &DeviceContext, _: &Splitter) -> Result<(), ObjectCreateError> { + if self.handle == vk::Image::null() { + let desc = self.info.get_description(); + let create_info = vk::ImageCreateInfo::builder() + .image_type(desc.spec.size.get_vulkan_type()) + .format(desc.spec.format.get_format()) + .extent(desc.spec.size.as_extent_3d()) + .mip_levels(desc.spec.size.get_mip_levels()) + .array_layers(desc.spec.size.get_array_layers()) + .samples(desc.spec.sample_count) + .tiling(vk::ImageTiling::OPTIMAL) // TODO we need some way to turn this linear + .usage(desc.usage_flags) + .sharing_mode(vk::SharingMode::EXCLUSIVE); + + self.handle = unsafe { + device.vk().create_image(&create_info.build(), None) + }?; + } + if self.allocation.is_none() { + self.allocation = Some(device.get_allocator().allocate_image_memory(self.handle, &self.strategy)?); + let alloc = self.allocation.as_ref().unwrap(); + + unsafe { + device.vk().bind_image_memory(self.handle, alloc.memory(), alloc.offset()) + }?; + } + Ok(()) + } + + fn abort(&mut self, device: &DeviceContext) { + if self.handle != vk::Image::null() { + unsafe { device.vk().destroy_image(self.handle, None) } + self.handle = vk::Image::null() + } + match self.allocation.take() { + Some(alloc) => { + device.get_allocator().free(alloc) + } + None => {} + } + } + + fn reduce(self) -> (ResourceObjectData, Option) { + if self.handle == vk::Image::null() || self.allocation.is_none() { + panic!("Incomplete Image object") + } + + let object = ResourceObjectData::Image { + handle: self.handle, + info: self.info + }; + + (object, self.allocation) + } +} + +struct ImageViewCreateMetadata { + info: Box, + image_set: Option, + image_id: id::ImageId, + handle: vk::ImageView, +} + +impl ImageViewCreateMetadata { + fn new(desc: ImageViewDescription, image_set: Option, image_id: id::ImageId, image_info: Arc) -> Self { + Self { + info: Box::new(ImageViewInfo::new(desc, image_id, image_info)), + image_set, + image_id, + handle: vk::ImageView::null(), + } + } + + fn create(&mut self, device: &DeviceContext, split: &Splitter) -> Result<(), ObjectCreateError> { + if self.handle == vk::ImageView::null() { + let image = match self.image_set.as_ref() { + Some(set) => { + unsafe { set.get_image_handle(self.image_id) } + } + None => { + let index = self.image_id.get_index() as usize; + match split.get(index).ok_or(ObjectCreateError::InvalidReference)? { + ResourceObjectCreateMetadata::Image(ImageCreateMetadata{ handle, .. }) => *handle, + _ => return Err(ObjectCreateError::InvalidReference) + } + } + }; + + let desc = self.info.get_description(); + let create_info = vk::ImageViewCreateInfo::builder() + .image(image) + .view_type(desc.view_type) + .format(desc.format.get_format()) + .components(desc.components) + .subresource_range(desc.subresource_range.as_vk_subresource_range()); + + self.handle = unsafe { + device.vk().create_image_view(&create_info, None)? + } + } + Ok(()) + } + + fn abort(&mut self, device: &DeviceContext) { + if self.handle != vk::ImageView::null() { + unsafe { device.vk().destroy_image_view(self.handle, None) } + self.handle = vk::ImageView::null() + } + } + + fn reduce(self) -> (ResourceObjectData, Option) { + if self.handle == vk::ImageView::null() { + panic!("Incomplete ImageView object") + } + + let object = ResourceObjectData::ImageView { + handle: self.handle, + info: self.info, + source_set: self.image_set + }; + + (object, None) + } +} + +enum ResourceObjectCreateMetadata { + Buffer(BufferCreateMetadata), + BufferView(BufferViewCreateMetadata), + Image(ImageCreateMetadata), + ImageView(ImageViewCreateMetadata), +} + +impl ResourceObjectCreateMetadata { + fn make_buffer(desc: BufferDescription, strategy: AllocationStrategy, group: SynchronizationGroup) -> Self { + Self::Buffer(BufferCreateMetadata::new(desc, strategy, group)) + } + + fn make_buffer_view(desc: BufferViewDescription, buffer_set: Option, buffer_id: id::BufferId, buffer_info: Arc) -> Self { + Self::BufferView(BufferViewCreateMetadata::new(desc, buffer_set, buffer_id, buffer_info)) + } + + fn make_image(desc: ImageDescription, strategy: AllocationStrategy, group: SynchronizationGroup) -> Self { + Self::Image(ImageCreateMetadata::new(desc, strategy, group)) + } + + fn make_image_view(desc: ImageViewDescription, image_set: Option, image_id: id::ImageId, image_info: Arc) -> Self { + Self::ImageView(ImageViewCreateMetadata::new(desc, image_set, image_id, image_info)) + } + + fn create(&mut self, device: &DeviceContext, split: &Splitter) -> Result<(), ObjectCreateError> { + match self { + ResourceObjectCreateMetadata::Buffer(data) => data.create(device, split), + ResourceObjectCreateMetadata::BufferView(data) => data.create(device, split), + ResourceObjectCreateMetadata::Image(data) => data.create(device, split), + ResourceObjectCreateMetadata::ImageView(data) => data.create(device, split), + } + } + + fn abort(&mut self, device: &DeviceContext) { + match self { + ResourceObjectCreateMetadata::Buffer(data) => data.abort(device), + ResourceObjectCreateMetadata::BufferView(data) => data.abort(device), + ResourceObjectCreateMetadata::Image(data) => data.abort(device), + ResourceObjectCreateMetadata::ImageView(data) => data.abort(device), + } + } + + fn reduce(self) -> (ResourceObjectData, Option) { + match self { + ResourceObjectCreateMetadata::Buffer(data) => data.reduce(), + ResourceObjectCreateMetadata::BufferView(data) => data.reduce(), + ResourceObjectCreateMetadata::Image(data) => data.reduce(), + ResourceObjectCreateMetadata::ImageView(data) => data.reduce(), + } + } +} + +enum ResourceObjectData { + Buffer { + handle: vk::Buffer, + info: Arc, + }, + BufferView { + handle: vk::BufferView, + info: Box, + source_set: Option, + }, + Image { + handle: vk::Image, + info: Arc, + }, + ImageView { + handle: vk::ImageView, + info: Box, + source_set: Option, + } +} + +impl ResourceObjectData { + pub fn destroy(self, device: &DeviceContext) { + match self { + ResourceObjectData::Buffer{ handle, .. } => { + unsafe { device.vk().destroy_buffer(handle, None) } + } + ResourceObjectData::BufferView{ handle, source_set, .. } => { + unsafe { device.vk().destroy_buffer_view(handle, None) } + drop(source_set); // Keep it alive until here + } + ResourceObjectData::Image{ handle, .. } => { + unsafe { device.vk().destroy_image(handle, None) } + } + ResourceObjectData::ImageView{ handle, source_set, .. } => { + unsafe { device.vk().destroy_image_view(handle, None) } + drop(source_set); // Keep it alive until here + } + } + } +} + +struct ResourceObjectSet { + set_id: ObjectSetId, + device: DeviceContext, + objects: Box<[ResourceObjectData]>, + allocations: Box<[Allocation]> +} + +impl Drop for ResourceObjectSet { + fn drop(&mut self) { + let objects = std::mem::replace(&mut self.objects, Box::new([])); + let allocations = std::mem::replace(&mut self.allocations, Box::new([])); + + for object in objects.into_vec().into_iter().rev() { + object.destroy(&self.device) + } + + let allocator = self.device.get_allocator(); + for allocation in allocations.into_vec() { + allocator.free(allocation); + } + } +} + +impl ObjectSetProvider for ResourceObjectSet { + fn get_id(&self) -> ObjectSetId { + self.set_id + } + + unsafe fn get_buffer_handle(&self, id: BufferId) -> vk::Buffer { + match self.objects.get(id.get_index() as usize).unwrap() { + ResourceObjectData::Buffer { handle, .. } => *handle, + _ => panic!("Id does not map to buffer") + } + } + + fn get_buffer_info(&self, id: BufferId) -> &Arc { + match self.objects.get(id.get_index() as usize).unwrap() { + ResourceObjectData::Buffer { info, .. } => info, + _ => panic!("Id does not map to buffer") + } + } + + unsafe fn get_buffer_view_handle(&self, id: BufferViewId) -> vk::BufferView { + match self.objects.get(id.get_index() as usize).unwrap() { + ResourceObjectData::BufferView { handle, .. } => *handle, + _ => panic!("Id does not map to buffer view") + } + } + + fn get_buffer_view_info(&self, id: BufferViewId) -> &BufferViewInfo { + match self.objects.get(id.get_index() as usize).unwrap() { + ResourceObjectData::BufferView { info, .. } => info.as_ref(), + _ => panic!("Id does not map to buffer view") + } + } + + unsafe fn get_image_handle(&self, id: ImageId) -> vk::Image { + match self.objects.get(id.get_index() as usize).unwrap() { + ResourceObjectData::Image { handle, .. } => *handle, + _ => panic!("Id does not map to image") + } + } + + fn get_image_info(&self, id: ImageId) -> &Arc { + match self.objects.get(id.get_index() as usize).unwrap() { + ResourceObjectData::Image { info, .. } => info, + _ => panic!("Id does not map to image") + } + } + + unsafe fn get_image_view_handle(&self, id: ImageViewId) -> vk::ImageView { + match self.objects.get(id.get_index() as usize).unwrap() { + ResourceObjectData::ImageView { handle, .. } => *handle, + _ => panic!("Id does not map to image view") + } + } + + fn get_image_view_info(&self, id: ImageViewId) -> &ImageViewInfo { + match self.objects.get(id.get_index() as usize).unwrap() { + ResourceObjectData::ImageView { info, .. } => info.as_ref(), + _ => panic!("Id does not map to image view") + } + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +#[cfg(test)] +mod tests { + use crate::objects::{BufferRange, Format, ImageSize, ImageSpec}; + use super::*; + use crate::test::make_headless_instance_device; + + #[test] + fn test_buffer_create() { + let (_, device) = make_headless_instance_device(); + + let group = SynchronizationGroup::new(device); + let mut builder = ResourceObjectSetBuilder::new(group.clone()); + + let desc1 = BufferDescription::new_simple(1024, vk::BufferUsageFlags::TRANSFER_DST); + let desc2 = BufferDescription::new_simple(512, vk::BufferUsageFlags::TRANSFER_SRC); + + let buffer1 = builder.add_default_gpu_only_buffer(desc1); + let buffer2 = builder.add_default_gpu_cpu_buffer(desc2); + + let set = builder.build().unwrap(); + + unsafe { + assert_ne!(set.get_buffer_handle(buffer1), vk::Buffer::null()); + assert_ne!(set.get_buffer_handle(buffer2), vk::Buffer::null()); + assert_ne!(set.get_buffer_handle(buffer1), set.get_buffer_handle(buffer2)); + + assert_eq!(set.get_buffer_info(buffer1).get_synchronization_group(), &group); + assert_eq!(set.get_buffer_info(buffer2).get_synchronization_group(), &group); + + assert_eq!(set.get_buffer_info(buffer1).get_description(), &desc1); + assert_eq!(set.get_buffer_info(buffer2).get_description(), &desc2); + } + + drop(set); + } + + #[test] + fn test_buffer_view_create() { + let (_, device) = make_headless_instance_device(); + + let group = SynchronizationGroup::new(device.clone()); + let mut builder1 = ResourceObjectSetBuilder::new(group.clone()); + + let buffer_desc1 = BufferDescription::new_simple(1024, vk::BufferUsageFlags::UNIFORM_TEXEL_BUFFER); + let buffer_desc2 = BufferDescription::new_simple(512, vk::BufferUsageFlags::UNIFORM_TEXEL_BUFFER); + + let buffer1 = builder1.add_default_gpu_only_buffer(buffer_desc1); + let buffer2 = builder1.add_default_gpu_cpu_buffer(buffer_desc2); + + let view_desc1 = BufferViewDescription::new_simple(BufferRange { offset: 256, length: 256 }, &Format::R16_UNORM); + let view_desc2 = BufferViewDescription::new_simple(BufferRange { offset: 0, length: 256 }, &Format::R8_UNORM); + + let view1 = builder1.add_internal_buffer_view(view_desc1, buffer1); + let view2 = builder1.add_internal_buffer_view(view_desc2, buffer2); + + let set1 = builder1.build().unwrap(); + + unsafe { + assert_ne!(set1.get_buffer_view_handle(view1), vk::BufferView::null()); + assert_ne!(set1.get_buffer_view_handle(view2), vk::BufferView::null()); + assert_ne!(set1.get_buffer_view_handle(view1), set1.get_buffer_view_handle(view2)); + + assert_eq!(set1.get_buffer_view_info(view1).get_synchronization_group(), &group); + assert_eq!(set1.get_buffer_view_info(view2).get_synchronization_group(), &group); + assert_eq!(set1.get_buffer_view_info(view1).get_description(), &view_desc1); + assert_eq!(set1.get_buffer_view_info(view2).get_description(), &view_desc2); + assert_eq!(set1.get_buffer_view_info(view1).get_source_buffer_id(), buffer1); + assert_eq!(set1.get_buffer_view_info(view2).get_source_buffer_id(), buffer2); + assert_eq!(set1.get_buffer_view_info(view1).get_source_buffer_info().get_description(), &buffer_desc1); + assert_eq!(set1.get_buffer_view_info(view2).get_source_buffer_info().get_description(), &buffer_desc2); + } + + let group2 = SynchronizationGroup::new(device); + let mut builder2 = ResourceObjectSetBuilder::new(group2.clone()); + + let view3 = builder2.add_external_buffer_view(view_desc2, set1.clone(), buffer1); + let view4 = builder2.add_external_buffer_view(view_desc1, set1.clone(), buffer2); + + let set2 = builder2.build().unwrap(); + + unsafe { + assert_ne!(set2.get_buffer_view_handle(view3), vk::BufferView::null()); + assert_ne!(set2.get_buffer_view_handle(view4), vk::BufferView::null()); + assert_ne!(set2.get_buffer_view_handle(view3), set2.get_buffer_view_handle(view4)); + + assert_eq!(set2.get_buffer_view_info(view3).get_synchronization_group(), &group); + assert_eq!(set2.get_buffer_view_info(view4).get_synchronization_group(), &group); + assert_eq!(set2.get_buffer_view_info(view3).get_description(), &view_desc2); + assert_eq!(set2.get_buffer_view_info(view4).get_description(), &view_desc1); + assert_eq!(set2.get_buffer_view_info(view3).get_source_buffer_id(), buffer1); + assert_eq!(set2.get_buffer_view_info(view4).get_source_buffer_id(), buffer2); + assert_eq!(set2.get_buffer_view_info(view3).get_source_buffer_info().get_description(), &buffer_desc1); + assert_eq!(set2.get_buffer_view_info(view4).get_source_buffer_info().get_description(), &buffer_desc2); + } + + drop(set1); + drop(set2); + } + + #[test] + fn test_image_create() { + let (_, device) = make_headless_instance_device(); + + let group = SynchronizationGroup::new(device); + let mut builder = ResourceObjectSetBuilder::new(group.clone()); + + let desc1 = ImageDescription::new_simple( + ImageSpec::new_single_sample( + ImageSize::make_2d(128, 128), + &Format::B8G8R8A8_SRGB, + ), + vk::ImageUsageFlags::SAMPLED, + ); + let desc2 = ImageDescription::new_simple( + ImageSpec::new_single_sample( + ImageSize::make_2d(256, 256), + &Format::B8G8R8A8_SRGB, + ), + vk::ImageUsageFlags::SAMPLED, + ); + + let image1 = builder.add_default_gpu_only_image(desc1); + let image2 = builder.add_default_gpu_only_image(desc2); + + let set = builder.build().unwrap(); + + unsafe { + assert_ne!(set.get_image_handle(image1), vk::Image::null()); + assert_ne!(set.get_image_handle(image2), vk::Image::null()); + assert_ne!(set.get_image_handle(image1), set.get_image_handle(image2)); + + assert_eq!(set.get_image_info(image1).get_synchronization_group(), &group); + assert_eq!(set.get_image_info(image2).get_synchronization_group(), &group); + + assert_eq!(set.get_image_info(image1).get_description(), &desc1); + assert_eq!(set.get_image_info(image2).get_description(), &desc2); + } + + drop(set); + } + + #[test] + fn test_image_view_create() { + let (_, device) = make_headless_instance_device(); + + let group = SynchronizationGroup::new(device.clone()); + let mut builder1 = ResourceObjectSetBuilder::new(group.clone()); + + let image_desc1 = ImageDescription::new_simple( + ImageSpec::new_single_sample( + ImageSize::make_2d(128, 128), + &Format::B8G8R8A8_SRGB, + ), + vk::ImageUsageFlags::SAMPLED, + ); + let image_desc2 = ImageDescription::new_simple( + ImageSpec::new_single_sample( + ImageSize::make_2d(256, 256), + &Format::B8G8R8A8_SRGB, + ), + vk::ImageUsageFlags::SAMPLED, + ); + + let image1 = builder1.add_default_gpu_only_image(image_desc1); + let image2 = builder1.add_default_gpu_only_image(image_desc2); + + let view_desc1 = ImageViewDescription::make_full(vk::ImageViewType::TYPE_2D, &Format::B8G8R8A8_SRGB, vk::ImageAspectFlags::COLOR); + let view_desc2 = ImageViewDescription::make_full(vk::ImageViewType::TYPE_2D, &Format::R8G8B8A8_SRGB, vk::ImageAspectFlags::COLOR); + + let view1 = builder1.add_internal_image_view(view_desc1, image1); + let view2 = builder1.add_internal_image_view(view_desc2, image2); + + let set1 = builder1.build().unwrap(); + + unsafe { + assert_ne!(set1.get_image_view_handle(view1), vk::ImageView::null()); + assert_ne!(set1.get_image_view_handle(view2), vk::ImageView::null()); + assert_ne!(set1.get_image_view_handle(view1), set1.get_image_view_handle(view2)); + + assert_eq!(set1.get_image_view_info(view1).get_synchronization_group(), &group); + assert_eq!(set1.get_image_view_info(view2).get_synchronization_group(), &group); + // TODO description equality test + assert_eq!(set1.get_image_view_info(view1).get_source_image_id(), image1); + assert_eq!(set1.get_image_view_info(view2).get_source_image_id(), image2); + assert_eq!(set1.get_image_view_info(view1).get_source_image_info().get_description(), &image_desc1); + assert_eq!(set1.get_image_view_info(view2).get_source_image_info().get_description(), &image_desc2); + } + + let group2 = SynchronizationGroup::new(device); + let mut builder2 = ResourceObjectSetBuilder::new(group2.clone()); + + let view3 = builder2.add_external_image_view(view_desc2, set1.clone(), image1); + let view4 = builder2.add_external_image_view(view_desc1, set1.clone(), image2); + + let set2 = builder2.build().unwrap(); + + unsafe { + assert_ne!(set2.get_image_view_handle(view3), vk::ImageView::null()); + assert_ne!(set2.get_image_view_handle(view4), vk::ImageView::null()); + assert_ne!(set2.get_image_view_handle(view3), set2.get_image_view_handle(view4)); + + assert_eq!(set2.get_image_view_info(view3).get_synchronization_group(), &group); + assert_eq!(set2.get_image_view_info(view4).get_synchronization_group(), &group); + // TODO description equality test + assert_eq!(set2.get_image_view_info(view3).get_source_image_id(), image1); + assert_eq!(set2.get_image_view_info(view4).get_source_image_id(), image2); + assert_eq!(set2.get_image_view_info(view3).get_source_image_info().get_description(), &image_desc1); + assert_eq!(set2.get_image_view_info(view4).get_source_image_info().get_description(), &image_desc2); + } + + drop(set1); + drop(set2); + } +} \ No newline at end of file diff --git a/src/objects/surface.rs b/src/objects/surface.rs new file mode 100644 index 0000000..04810d9 --- /dev/null +++ b/src/objects/surface.rs @@ -0,0 +1,169 @@ +use std::cmp::Ordering; +use std::sync::{Arc, Mutex, MutexGuard}; + +use ash::vk; + +use crate::objects::id::{ObjectSetId, SurfaceId}; +use crate::rosella::InstanceContext; + +/// Trait that provides access to a surface object. +/// +/// Since many possible surface objects exits and management of these can differ this trait is +/// used to abstract those differences away. Rosella will only access surfaces using a trait object +/// of this type. Once the trait object is dropped it may assume that the surface is no longer used +/// by rosella and is safe to be destroyed. +/// +/// Note: While dropping of a surface typically is a rare occurrence it *may* happen synchronously +/// with other engine operations. As such extensive computations or blocking operations should be +/// avoided in the drop function. +pub trait SurfaceProvider : Sync { + fn get_handle(&self) -> vk::SurfaceKHR; +} + +struct SurfaceImpl { + id: SurfaceId, + handle: vk::SurfaceKHR, + swapchain_info: Mutex, + + #[allow(unused)] // Only reason we need this field is to keep the provider alive. + surface: Box, +} + +/// Wrapper struct for surfaces. +/// +/// Provides access to a surface provider using a arc. +#[derive(Clone)] +pub struct Surface(Arc); + +impl Surface { + pub fn new(surface: Box) -> Self { + Self(Arc::new(SurfaceImpl{ + id: SurfaceId::new(ObjectSetId::new(), 0), + handle: surface.get_handle(), + swapchain_info: Mutex::new(SurfaceSwapchainInfo::None), + surface + })) + } + + pub fn get_handle(&self) -> vk::SurfaceKHR { + self.0.handle + } + + pub fn get_id(&self) -> SurfaceId { + self.0.id + } + + /// Locks access to the information for the current access. This lock **must** be held when + /// creating or destroying a swapchain associated with this surface. This is, unless otherwise, + /// noted done inside object sets creating swapchains. + pub fn lock_swapchain_info(&self) -> MutexGuard { + self.0.swapchain_info.lock().unwrap() + } +} + +impl PartialEq for Surface { + fn eq(&self, other: &Self) -> bool { + self.0.id.eq(&other.0.id) + } +} + +impl Eq for Surface { +} + +impl PartialOrd for Surface { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.id.partial_cmp(&other.0.id) + } +} + +impl Ord for Surface { + fn cmp(&self, other: &Self) -> Ordering { + self.0.id.cmp(&other.0.id) + } +} + +/// Contains information about the current non retired swapchain associated with the surface. +pub enum SurfaceSwapchainInfo { + Some { + handle: vk::SwapchainKHR, + }, + None +} + +impl SurfaceSwapchainInfo { + pub fn get_current_handle(&self) -> Option { + match self { + SurfaceSwapchainInfo::Some { handle, .. } => Some(*handle), + SurfaceSwapchainInfo::None => None + } + } + + pub fn set_swapchain(&mut self, handle: vk::SwapchainKHR) { + *self = SurfaceSwapchainInfo::Some { + handle + }; + } + + pub fn clear(&mut self) { + *self = SurfaceSwapchainInfo::None; + } +} + +pub struct SurfaceCapabilities { + presentable_queues: Box<[u32]>, + surface_formats: Box<[vk::SurfaceFormatKHR]>, + present_modes: Box<[vk::PresentModeKHR]>, + capabilities: vk::SurfaceCapabilitiesKHR, +} + +impl SurfaceCapabilities { + pub fn new(instance: &InstanceContext, physical_device: vk::PhysicalDevice, surface: vk::SurfaceKHR) -> Option { + let surface_fn = instance.get_extension::()?; + let family_count = unsafe { + instance.vk().get_physical_device_queue_family_properties(physical_device).len() + } as u32; + + let presentable_queues = (0..family_count).filter(|family| unsafe { + surface_fn.get_physical_device_surface_support(physical_device, *family, surface).unwrap() + }).collect::>().into_boxed_slice(); + + if presentable_queues.len() == 0 { + return None; + } + + let capabilities = unsafe { + surface_fn.get_physical_device_surface_capabilities(physical_device, surface) + }.ok()?; + + let surface_formats = unsafe { + surface_fn.get_physical_device_surface_formats(physical_device, surface) + }.ok()?.into_boxed_slice(); + + let present_modes = unsafe { + surface_fn.get_physical_device_surface_present_modes(physical_device, surface) + }.ok()?.into_boxed_slice(); + + Some(Self{ + presentable_queues, + surface_formats, + present_modes, + capabilities, + }) + } + + pub fn get_capabilities(&self) -> &vk::SurfaceCapabilitiesKHR { + &self.capabilities + } + + pub fn get_presentable_queue_families(&self) -> &[u32] { + self.presentable_queues.as_ref() + } + + pub fn get_surface_formats(&self) -> &[vk::SurfaceFormatKHR] { + self.surface_formats.as_ref() + } + + pub fn get_present_modes(&self) -> &[vk::PresentModeKHR] { + self.present_modes.as_ref() + } +} \ No newline at end of file diff --git a/src/objects/swapchain.rs b/src/objects/swapchain.rs index a886511..559bd55 100644 --- a/src/objects/swapchain.rs +++ b/src/objects/swapchain.rs @@ -66,10 +66,25 @@ impl SwapchainImageSpec { #[derive(Copy, Clone)] #[non_exhaustive] pub struct SwapchainCreateDesc { + pub min_image_count: u32, pub image_spec: SwapchainImageSpec, pub usage: vk::ImageUsageFlags, pub pre_transform: vk::SurfaceTransformFlagsKHR, pub composite_alpha: vk::CompositeAlphaFlagsKHR, pub present_mode: vk::PresentModeKHR, pub clipped: bool, +} + +impl SwapchainCreateDesc { + pub fn make(image_spec: SwapchainImageSpec, min_image_count: u32, usage: vk::ImageUsageFlags, present_mode: vk::PresentModeKHR) -> Self { + SwapchainCreateDesc { + min_image_count, + image_spec, + usage, + pre_transform: vk::SurfaceTransformFlagsKHR::IDENTITY, + composite_alpha: vk::CompositeAlphaFlagsKHR::OPAQUE, + present_mode, + clipped: false, + } + } } \ No newline at end of file diff --git a/src/objects/swapchain_object_set.rs b/src/objects/swapchain_object_set.rs new file mode 100644 index 0000000..a79e279 --- /dev/null +++ b/src/objects/swapchain_object_set.rs @@ -0,0 +1,549 @@ +use std::any::Any; +use std::sync::Arc; +use ash::prelude::VkResult; +use ash::vk; +use ash::vk::{Fence, Image, ImageView, Semaphore, SwapchainKHR}; +use crate::objects::{id, ObjectSet, SynchronizationGroup}; +use crate::objects::id::{FenceId, ImageId, ImageViewId, ObjectSetId, SemaphoreId, SurfaceId, SwapchainId}; +use crate::objects::image::{ImageDescription, ImageInfo, ImageViewDescription, ImageViewInfo}; +use crate::objects::object_set::ObjectSetProvider; +use crate::objects::swapchain::SwapchainCreateDesc; +use crate::rosella::DeviceContext; + +/// Swapchain object sets manage the creation of swapchains and have utilities for some common +/// objects needed for each image. +/// +/// Derivative objects can be added in which case a object is created for each swapchain image. +/// ImageViews, binary Semaphores and Fences are currently supported as derivative objects. +/// +/// The swapchain itself is created during the creation of the builder (this is necessary because +/// the builder needs to know the number of images that are in the swapchain). Just like with +/// resource object sets the derivative objects are only created during the +/// [`SwapchainObjectSetBuilder::build`] call. +/// +/// # Examples +/// +/// ``` +/// # use rosella_rs::objects::swapchain::{SwapchainCreateDesc, SwapchainImageSpec}; +/// # use rosella_rs::objects::{Format, ImageViewDescription, SwapchainObjectSetBuilder}; +/// use ash::vk; +/// +/// // Create a builder. The swapchain will be immediately created. +/// let mut builder = SwapchainObjectSetBuilder::new( +/// device, +/// surface_id, +/// SwapchainCreateDesc::make( +/// SwapchainImageSpec::make( +/// &Format::R8G8B8A8_SRGB, +/// vk::ColorSpaceKHR::SRGB_NONLINEAR, +/// 1920, 1080 +/// ), +/// 1, +/// vk::ImageUsageFlags::SAMPLED, +/// vk::PresentModeKHR::MAILBOX +/// ), +/// None +/// ).unwrap(); +/// +/// // We can query information about the already created swapchain +/// let swapchain_id = builder.get_swapchain_id(); +/// let image_count = builder.get_image_ids().len(); +/// +/// // Add a image view. One will be created for each image of the swapchain +/// let image_views = builder.add_views(ImageViewDescription::make_full( +/// vk::ImageViewType::TYPE_2D, +/// &Format::R8G8B8A8_SRGB, +/// vk::ImageAspectFlags::COLOR +/// )); +/// +/// // Similar to image views one semaphore will be created for each swapchain image +/// let semaphores = builder.add_binary_semaphores(); +/// +/// // During the build call all derivative objects will be created. +/// let object_set = builder.build().unwrap(); +/// +/// // Now we can access the objects and swapchain +/// let swapchain = unsafe { object_set.get_swapchain_handle(swapchain_id) }; +/// for view in image_views.iter() { +/// unsafe { object_set.get_image_view_handle(*view) }; +/// } +/// +/// // The swapchain and derivative objects will be destroyed when the object set is dropped. The +/// // object set type uses Arc internally so it can be cloned and the objects will only be dropped +/// // when all references have been dropped. +/// ``` +pub struct SwapchainObjectSetBuilder { + device: DeviceContext, + set_id: ObjectSetId, + surface: SurfaceId, + swapchain: vk::SwapchainKHR, + images: Box<[SwapchainImage]>, + image_desc: ImageDescription, + derivatives: Vec, +} + +impl SwapchainObjectSetBuilder { + /// Creates a new swapchain object set builder. + /// + /// The swapchain will be immediately created. If a synchronization group is specified it will + /// be used for all images. Otherwise a new synchronization group will be created for each + /// individual image. + pub fn new(device: DeviceContext, surface_id: SurfaceId, desc: SwapchainCreateDesc, synchronization_group: Option) -> VkResult { + let swapchain_fn = device.get_extension::().unwrap(); + + let surface = device.get_surface(surface_id).unwrap(); + let mut swapchain_info = surface.lock_swapchain_info(); + + let old_swapchain = swapchain_info.get_current_handle().unwrap_or(SwapchainKHR::null()); + + let create_info = vk::SwapchainCreateInfoKHR::builder() + .surface(surface.get_handle()) + .min_image_count(desc.min_image_count) + .image_format(desc.image_spec.format.get_format()) + .image_color_space(desc.image_spec.color_space) + .image_extent(desc.image_spec.extent) + .image_array_layers(desc.image_spec.array_layers) + .image_usage(desc.usage) + .image_sharing_mode(vk::SharingMode::EXCLUSIVE) + .pre_transform(desc.pre_transform) + .composite_alpha(desc.composite_alpha) + .present_mode(desc.present_mode) + .clipped(desc.clipped) + .old_swapchain(old_swapchain); + + let new_swapchain = unsafe { + swapchain_fn.create_swapchain(&create_info, None) + }?; + + swapchain_info.set_swapchain(new_swapchain); + + let images = unsafe { + swapchain_fn.get_swapchain_images(new_swapchain) + + }.map_err(|err| { + // If there was an error destroy the swapchain and clear the surface swapchain info + unsafe { + swapchain_fn.destroy_swapchain(new_swapchain, None); + } + swapchain_info.clear(); + + err + })?; + + // Need to keep this alive until we are done with all operations that could fail + drop(swapchain_info); + + let image_desc = ImageDescription { + spec: desc.image_spec.as_image_spec(), + usage_flags: desc.usage, + }; + + let images : Box<_> = images.into_iter().map(|image| { + let group = match &synchronization_group { + None => SynchronizationGroup::new(device.clone()), + Some(group) => group.clone(), + }; + + SwapchainImage { + info: Arc::new(ImageInfo::new(image_desc, group)), + handle: image, + } + }).collect(); + + // After this point errors are handled by the drop function of the SwapchainObjectSetBuilder + Ok(Self { + device, + set_id: ObjectSetId::new(), + surface: surface_id, + swapchain: new_swapchain, + images, + image_desc, + derivatives: Vec::new(), + }) + } + + pub fn get_image_description(&self) -> &ImageDescription { + &self.image_desc + } + + pub fn get_swapchain_id(&self) -> SwapchainId { + SwapchainId::new(self.set_id, 0) + } + + pub fn get_image_ids(&self) -> Box<[ImageId]> { + (0..self.images.len()).map(|index| ImageId::new(self.set_id, index as u16)).collect() + } + + fn get_next_index(&self) -> u16 { + let index = self.derivatives.len(); + if index > u16::MAX as usize { + panic!("Too many objects in object set"); + } + index as u16 + } + + /// Adds a set of image views for each image of the swapchain + pub fn add_views(&mut self, desc: ImageViewDescription) -> Box<[ImageViewId]> { + self.derivatives.reserve(self.images.len()); + let mut ids = Vec::with_capacity(self.images.len()); + + for (index, image) in self.images.as_ref().iter().enumerate() { + ids.push(ImageViewId::new(self.set_id, self.get_next_index())); + + let image_id = ImageId::new(self.set_id, index as u16); + self.derivatives.push(DerivativeData::make_image_view(desc, image_id, image.info.clone())); + } + + ids.into_boxed_slice() + } + + /// Adds a set of binary semaphores for each image of the swapchain + pub fn add_binary_semaphores(&mut self) -> Box<[SemaphoreId]> { + self.derivatives.reserve(self.images.len()); + let mut ids = Vec::with_capacity(self.images.len()); + + for _ in self.images.as_ref() { + ids.push(SemaphoreId::new(self.set_id, self.get_next_index())); + self.derivatives.push(DerivativeData::make_binary_semaphore()) + } + + ids.into_boxed_slice() + } + + /// Adds a set of fences for each image of the swapchain + pub fn add_fences(&mut self) -> Box<[FenceId]> { + self.derivatives.reserve(self.images.len()); + let mut ids = Vec::with_capacity(self.images.len()); + + for _ in self.images.as_ref() { + ids.push(FenceId::new(self.set_id, self.get_next_index())); + self.derivatives.push(DerivativeData::make_fence()) + } + + ids.into_boxed_slice() + } + + fn create(&mut self) -> Result<(), vk::Result> { + for derivative in &mut self.derivatives { + derivative.create(&self.device, &self.images)?; + } + + Ok(()) + } + + fn destroy(&mut self) { + for derivative in &mut self.derivatives { + derivative.destroy(&self.device); + } + } + + pub fn build(mut self) -> Result { + if let Err(err) = self.create() { + self.destroy(); + return Err(err); + } + + // This is beyond ugly but necessary since we implement drop + Ok(ObjectSet::new(SwapchainObjectSet { + device: self.device.clone(), + set_id: self.set_id, + surface: self.surface, + swapchain: std::mem::replace(&mut self.swapchain, vk::SwapchainKHR::null()), + images: std::mem::replace(&mut self.images, Box::new([])), + derivatives: std::mem::replace(&mut self.derivatives, Vec::new()).into_boxed_slice(), + })) + } +} + +impl Drop for SwapchainObjectSetBuilder { + fn drop(&mut self) { + if self.swapchain != vk::SwapchainKHR::null() { + let swapchain_fn = self.device.get_extension::().unwrap(); + + let surface = self.device.get_surface(self.surface).unwrap(); + let mut swapchain_info = surface.lock_swapchain_info(); + + unsafe { + swapchain_fn.destroy_swapchain(self.swapchain, None) + }; + + if swapchain_info.get_current_handle() == Some(self.swapchain) { + swapchain_info.clear(); + } + self.swapchain = vk::SwapchainKHR::null(); + } + } +} + +struct ImageViewData { + info: Box, + handle: vk::ImageView, +} + +impl ImageViewData { + fn new(desc: ImageViewDescription, image_id: id::ImageId, image_info: Arc) -> Self { + Self { + info: Box::new(ImageViewInfo::new(desc, image_id, image_info)), + handle: vk::ImageView::null(), + } + } + + fn create(&mut self, device: &DeviceContext, images: &Box<[SwapchainImage]>) -> Result<(), vk::Result> { + if self.handle == vk::ImageView::null() { + let index = self.info.get_source_image_id().get_index() as usize; + + let description = self.info.get_description(); + + let info = vk::ImageViewCreateInfo::builder() + .image(images.get(index).unwrap().handle) + .view_type(description.view_type) + .format(description.format.get_format()) + .components(description.components) + .subresource_range(description.subresource_range.as_vk_subresource_range()); + + self.handle = unsafe { + device.vk().create_image_view(&info, None) + }?; + } + + Ok(()) + } + + fn destroy(&mut self, device: &DeviceContext) { + if self.handle != vk::ImageView::null() { + unsafe { device.vk().destroy_image_view(self.handle, None) }; + self.handle = vk::ImageView::null(); + } + } +} + +struct BinarySemaphoreData { + handle: vk::Semaphore, +} + +impl BinarySemaphoreData { + fn new() -> Self { + Self { + handle: vk::Semaphore::null(), + } + } + + fn create(&mut self, device: &DeviceContext) -> Result<(), vk::Result> { + if self.handle == vk::Semaphore::null() { + let info = vk::SemaphoreCreateInfo::builder(); + + let handle = unsafe { + device.vk().create_semaphore(&info, None) + }?; + self.handle = handle; + } + + Ok(()) + } + + fn destroy(&mut self, device: &DeviceContext) { + if self.handle != vk::Semaphore::null() { + unsafe { device.vk().destroy_semaphore(self.handle, None) }; + self.handle = vk::Semaphore::null(); + } + } +} + +struct FenceData { + handle: vk::Fence, +} + +impl FenceData { + fn new() -> Self { + Self { + handle: vk::Fence::null(), + } + } + + fn create(&mut self, device: &DeviceContext) -> Result<(), vk::Result> { + if self.handle == vk::Fence::null() { + let info = vk::FenceCreateInfo::builder(); + + let handle = unsafe { + device.vk().create_fence(&info, None) + }?; + self.handle = handle; + } + + Ok(()) + } + + fn destroy(&mut self, device: &DeviceContext) { + if self.handle != vk::Fence::null() { + unsafe { device.vk().destroy_fence(self.handle, None) }; + self.handle = vk::Fence::null(); + } + } +} + +enum DerivativeData { + ImageView(ImageViewData), + BinarySemaphore(BinarySemaphoreData), + Fence(FenceData), +} + +impl DerivativeData { + fn make_image_view(desc: ImageViewDescription, image_id: id::ImageId, image_info: Arc) -> Self { + Self::ImageView(ImageViewData::new(desc, image_id, image_info)) + } + + fn make_binary_semaphore() -> Self { + Self::BinarySemaphore(BinarySemaphoreData::new()) + } + + fn make_fence() -> Self { + Self::Fence(FenceData::new()) + } + + fn create(&mut self, device: &DeviceContext, images: &Box<[SwapchainImage]>) -> Result<(), vk::Result> { + match self { + DerivativeData::ImageView(data) => data.create(device, images), + DerivativeData::BinarySemaphore(data) => data.create(device), + DerivativeData::Fence(data) => data.create(device) + } + } + + fn destroy(&mut self, device: &DeviceContext) { + match self { + DerivativeData::ImageView(data) => data.destroy(device), + DerivativeData::BinarySemaphore(data) => data.destroy(device), + DerivativeData::Fence(data) => data.destroy(device), + } + } +} + +struct SwapchainImage { + info: Arc, + handle: vk::Image, +} + +struct SwapchainObjectSet { + device: DeviceContext, + set_id: ObjectSetId, + surface: SurfaceId, + swapchain: vk::SwapchainKHR, + images: Box<[SwapchainImage]>, + derivatives: Box<[DerivativeData]>, +} + +impl SwapchainObjectSet { + +} + +impl ObjectSetProvider for SwapchainObjectSet { + fn get_id(&self) -> ObjectSetId { + self.set_id + } + + unsafe fn get_image_handle(&self, id: ImageId) -> Image { + if id.get_set_id() != self.set_id { + panic!("Image belongs to different object set"); + } + + let index = id.get_index() as usize; + self.images.get(index).unwrap().handle + } + + fn get_image_info(&self, id: ImageId) -> &Arc { + if id.get_set_id() != self.set_id { + panic!("Image belongs to different object set"); + } + + let index = id.get_index() as usize; + &self.images.get(index).unwrap().info + } + + unsafe fn get_image_view_handle(&self, id: ImageViewId) -> ImageView { + if id.get_set_id() != self.set_id { + panic!("ImageView belongs to different object set"); + } + + let index = id.get_index() as usize; + match self.derivatives.get(index).unwrap() { + DerivativeData::ImageView(data) => data.handle, + _ => panic!("Id does not map to image view"), + } + } + + fn get_image_view_info(&self, id: ImageViewId) -> &ImageViewInfo { + if id.get_set_id() != self.set_id { + panic!("ImageView belongs to different object set"); + } + + let index = id.get_index() as usize; + match self.derivatives.get(index).unwrap() { + DerivativeData::ImageView(data) => data.info.as_ref(), + _ => panic!("Id does not map to image view"), + } + } + + unsafe fn get_swapchain_handle(&self, id: SwapchainId) -> SwapchainKHR { + if id != SwapchainId::new(self.set_id, 0) { + panic!("Invalid SwapchainId") + } + + self.swapchain + } + + unsafe fn get_semaphore_handle(&self, id: SemaphoreId) -> Semaphore { + if id.get_set_id() != self.set_id { + panic!("Semaphore belongs to different object set"); + } + + let index = id.get_index() as usize; + match self.derivatives.get(index).unwrap() { + DerivativeData::BinarySemaphore(data) => data.handle, + _ => panic!("Id does not map to semaphore"), + } + } + + unsafe fn get_fence_handle(&self, id: FenceId) -> Fence { + if id.get_set_id() != self.set_id { + panic!("Fence belongs to different object set"); + } + + let index = id.get_index() as usize; + match self.derivatives.get(index).unwrap() { + DerivativeData::Fence(data) => data.handle, + _ => panic!("Id does not map to fence"), + } + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl Drop for SwapchainObjectSet { + fn drop(&mut self) { + for derivative in self.derivatives.as_mut() { + derivative.destroy(&self.device); + } + + if self.swapchain != vk::SwapchainKHR::null() { + let swapchain_fn = self.device.get_extension::().unwrap(); + + let surface = self.device.get_surface(self.surface).unwrap(); + let mut swapchain_info = surface.lock_swapchain_info(); + + unsafe { + swapchain_fn.destroy_swapchain(self.swapchain, None) + }; + + if swapchain_info.get_current_handle() == Some(self.swapchain) { + swapchain_info.clear(); + } + self.swapchain = vk::SwapchainKHR::null(); + } + } +} + +#[cfg(test)] +mod tests { + // TODO how on earth do we test this??? +} \ No newline at end of file diff --git a/src/objects/manager/synchronization_group.rs b/src/objects/synchronization_group.rs similarity index 81% rename from src/objects/manager/synchronization_group.rs rename to src/objects/synchronization_group.rs index 4e185ba..37aa7b8 100644 --- a/src/objects/manager/synchronization_group.rs +++ b/src/objects/synchronization_group.rs @@ -3,10 +3,9 @@ use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::sync::{Arc, LockResult, Mutex, MutexGuard}; -use crate::util::id::GlobalId; -use super::ObjectManager; - use ash::vk; +use crate::device::DeviceContext; +use crate::UUID; // Internal struct containing the semaphore payload and metadata struct SyncData { @@ -30,17 +29,27 @@ impl SyncData { // Internal implementation of the synchronization group struct SynchronizationGroupImpl { - group_id: GlobalId, + group_id: UUID, sync_data: Mutex, - manager: ObjectManager, + device: DeviceContext, } impl SynchronizationGroupImpl { - fn new(manager: ObjectManager, semaphore: vk::Semaphore) -> Self { - Self{ group_id: GlobalId::new(), sync_data: Mutex::new(SyncData{ semaphore, last_access: 0u64 }), manager } + fn new(device: DeviceContext) -> Self { + let mut timeline_info = vk::SemaphoreTypeCreateInfo::builder() + .semaphore_type(vk::SemaphoreType::TIMELINE) + .initial_value(1); + + let info = vk::SemaphoreCreateInfo::builder().push_next(&mut timeline_info); + + let semaphore = unsafe { + device.vk().create_semaphore(&info.build(), None).unwrap() + }; + + Self{ group_id: UUID::new(), sync_data: Mutex::new(SyncData{ semaphore, last_access: 0u64 }), device } } - fn get_group_id(&self) -> GlobalId { + fn get_group_id(&self) -> UUID { self.group_id } @@ -51,7 +60,9 @@ impl SynchronizationGroupImpl { impl Drop for SynchronizationGroupImpl { fn drop(&mut self) { - self.manager.destroy_semaphore(self.sync_data.get_mut().unwrap().semaphore) + unsafe { + self.device.vk().destroy_semaphore(self.sync_data.get_mut().unwrap().semaphore, None) + } } } @@ -88,17 +99,17 @@ impl Debug for SynchronizationGroup { pub struct SynchronizationGroup(Arc); impl SynchronizationGroup { - pub(super) fn new(manager: ObjectManager, semaphore: vk::Semaphore) -> Self { - Self(Arc::new(SynchronizationGroupImpl::new(manager, semaphore))) + pub fn new(device: DeviceContext) -> Self { + Self(Arc::new(SynchronizationGroupImpl::new(device))) } - pub fn get_group_id(&self) -> GlobalId { + pub fn get_group_id(&self) -> UUID { self.0.get_group_id() } - /// Returns the object manager managing this synchronization group - pub fn get_manager(&self) -> &ObjectManager { - &self.0.manager + /// Returns the device of the group + pub fn get_device(&self) -> &DeviceContext { + &self.0.device } /// Enqueues an access to the resources protected by this group. diff --git a/src/rosella.rs b/src/rosella.rs index 746f20f..2bceb79 100644 --- a/src/rosella.rs +++ b/src/rosella.rs @@ -4,17 +4,17 @@ use crate::init::instance::{create_instance, InstanceCreateError}; use crate::window::{RosellaSurface, RosellaWindow}; use crate::init::rosella_features::WindowSurface; -use crate::objects::ObjectManager; pub use crate::instance::VulkanVersion; pub use crate::instance::InstanceContext; pub use crate::device::DeviceContext; +use crate::objects::id::SurfaceId; +use crate::objects::surface::Surface; pub struct Rosella { pub instance: InstanceContext, - pub surface: RosellaSurface, + pub surface: SurfaceId, pub device: DeviceContext, - pub object_manager: ObjectManager, } #[derive(Debug)] @@ -45,20 +45,18 @@ impl Rosella { let instance = create_instance(&mut registry, application_name, 0)?; - let surface = RosellaSurface::new(instance.vk(), &instance.get_entry(), window); + let surface = Surface::new(Box::new(RosellaSurface::new(&instance, window))); + let surface_id = surface.get_id(); - let device = create_device(&mut registry, instance.clone())?; + let device = create_device(&mut registry, instance.clone(), &[surface])?; let elapsed = now.elapsed(); println!("Instance & Device Initialization took: {:.2?}", elapsed); - let object_manager = ObjectManager::new(device.clone()); - Ok(Rosella { instance, - surface, + surface: surface_id, device, - object_manager, }) } diff --git a/src/util/extensions.rs b/src/util/extensions.rs index 23356f0..5799bc1 100644 --- a/src/util/extensions.rs +++ b/src/util/extensions.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use ash::{Entry, Instance}; +use ash::{Device, Entry, Instance}; use crate::NamedUUID; use paste::paste; use crate::util::id::UUID; @@ -92,14 +92,15 @@ macro_rules! make_vk_extension_info { } $(impl VkExtensionInfo for $struct_name { - const UUID: NamedUUID = NamedUUID::new_const(stringify!($string_name)); + const UUID: NamedUUID = NamedUUID::from_str(stringify!($string_name)); })+ } } make_vk_extension_info!( - ash::extensions::khr::Swapchain, VK_KHR_Swapchain; ash::extensions::khr::GetPhysicalDeviceProperties2, VK_KHR_get_physical_device_properties2; + ash::extensions::khr::Surface, VK_KHR_surface; + ash::extensions::khr::Swapchain, VK_KHR_swapchain; ash::extensions::khr::TimelineSemaphore, VK_KHR_timeline_semaphore; ash::extensions::ext::DebugUtils, VK_EXT_debug_utils ); @@ -110,12 +111,24 @@ impl InstanceExtensionLoader for ash::extensions::khr::GetPhysicalDeviceProperti } } +impl InstanceExtensionLoader for ash::extensions::khr::Surface { + fn load_extension(function_set: &mut ExtensionFunctionSet, entry: &Entry, instance: &Instance) { + function_set.add(Box::new(ash::extensions::khr::Surface::new(entry, instance))) + } +} + impl InstanceExtensionLoader for ash::extensions::ext::DebugUtils { fn load_extension(function_set: &mut ExtensionFunctionSet, entry: &Entry, instance: &Instance) { function_set.add(Box::new(ash::extensions::ext::DebugUtils::new(entry, instance))) } } +impl DeviceExtensionLoader for ash::extensions::khr::Swapchain { + fn load_extension(function_set: &mut ExtensionFunctionSet, _: &Entry, instance: &Instance, device: &Device) { + function_set.add(Box::new(ash::extensions::khr::Swapchain::new(instance, device))) + } +} + impl DeviceExtensionLoader for ash::extensions::khr::TimelineSemaphore { fn load_extension(function_set: &mut ExtensionFunctionSet, _: &Entry, instance: &Instance, device: &ash::Device) { function_set.add(Box::new(ash::extensions::khr::TimelineSemaphore::new(instance, device))) diff --git a/src/util/id.rs b/src/util/id.rs index 35aab39..842c908 100644 --- a/src/util/id.rs +++ b/src/util/id.rs @@ -1,150 +1,45 @@ /// Utilities for globally unique identifiers. -/// -/// A UUID is made up of 2 parts. A global id and a local id. The global id acts as a identifier for -/// local address spaces allowing systems to create their own generation methods for local ids while -/// retaining global uniqueness. +use std::cell::RefCell; use std::cmp::Ordering; use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::num::NonZeroU64; -use std::sync::Arc; -use std::sync::atomic::AtomicU64; +use std::sync::{Arc, Mutex}; -/// A global id backed by a 64 bit value. -/// -/// Global ids are guaranteed to be globally unique. They are generated by an incrementing u64 bit -/// internal counter and will always be non zero. -/// -/// # Examples -/// -/// ``` -/// use rosella_rs::util::id::GlobalId; -/// -/// // Creates a new global id -/// let id = GlobalId::new(); -/// -/// // This is still the same id -/// let same_id = id.clone(); -/// -/// assert_eq!(id, same_id); -/// -/// // Creates a new different global id -/// let other_id = GlobalId::new(); -/// -/// assert_ne!(id, other_id); -/// -/// // 0 is a niche so Options are free -/// assert_eq!(8, std::mem::size_of::>()); -/// ``` -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct GlobalId(NonZeroU64); - -// Need to reserve value 1 for the NamedId address space -static NEXT_GLOBAL_ID: AtomicU64 = AtomicU64::new(2u64); - -impl GlobalId { - /// Creates a new globally unique id - /// - /// # Panics - /// - /// This function will panic if the internal 64bit counter overflows. - pub fn new() -> Self { - let next = NEXT_GLOBAL_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed); +use lazy_static::lazy_static; - // Give it some padding to allow panics to propagate. Its still 63bits big - if next > (u64::MAX / 2u64) { - panic!("GlobalId overflow!"); - } - - GlobalId(NonZeroU64::new(next).unwrap()) - } - - /// Creates a global id from a raw 64bit value. - /// - /// This value **must** have previously been created by a call to [`GlobalId::new()`] otherwise - /// this **will** result in undefined behaviour. - /// - /// # Panics - /// - /// The function will panic if the id is `0`. - pub const fn from_raw(id: u64) -> Self { - if id == 0u64 { - panic!("Id must not be 0"); - } +use crate::util::rand::Xoshiro256PlusPlus; - unsafe { // Need const unwrap - Self(NonZeroU64::new_unchecked(id)) - } - } +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct UUID(NonZeroU64); - /// Returns the raw 64bit global id. - pub fn get_raw(&self) -> u64 { - self.0.get() - } +lazy_static! { + static ref UUID_SEEDER : Mutex = Mutex::new(Xoshiro256PlusPlus::from_seed([1u64, 1u64, 1u64, 1u64])); } -impl Into for GlobalId { - fn into(self) -> u64 { - self.get_raw() - } -} +thread_local! { + static THREAD_UUID_SEEDER : RefCell = { + let mut seeder = UUID_SEEDER.lock().unwrap(); + seeder.jump(); -impl Debug for GlobalId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&*format!("GlobalId({:#16X})", self.0)) + RefCell::new(*seeder) } } -/// A local id. -/// -/// While global ids are guaranteed to be globally unique, local ids must not be and can be -/// generated in any way. The pair of a global id and a local id creates a globally unique -/// identifier. -/// -/// Local ids are non zero u64 values. -/// -/// # Examples -/// -/// ``` -/// use rosella_rs::util::id::LocalId; -/// -/// // Creates a new local id with a value of 1 -/// let id = LocalId::from_raw(1u64); -/// -/// let same_id = id.clone(); -/// assert_eq!(id, same_id); -/// -/// // Local ids may not be globally unique -/// let still_same_id = LocalId::from_raw(1u64); -/// assert_eq!(id, still_same_id); -/// ``` -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct LocalId(NonZeroU64); - -impl LocalId { - /// Creates a local id for a raw value. - /// - /// The value must not be 0. - pub const fn from_raw(value: u64) -> Self { - if value == 0u64 { - panic!("Local id must not be 0"); - } +impl UUID { + pub fn new() -> Self { + let mut seeder = UUID_SEEDER.lock().unwrap(); + let id = seeder.find(|&v| v != 0u64).unwrap(); - unsafe { // Need const unwrap - Self(NonZeroU64::new_unchecked(value)) - } + Self(NonZeroU64::new(id).unwrap()) } - /// Creates a local id from a hash value. if the hash is 0 it will be set to 1. - pub const fn from_hash(mut hash: u64) -> Self { - if hash == 0u64 { - hash = 1u64; - } - - unsafe { // Need const unwrap - Self(NonZeroU64::new_unchecked(hash)) + pub const fn from_raw(id: u64) -> Self { + if id == 0u64 { + panic!("Zero id") } + Self(unsafe { NonZeroU64::new_unchecked(id) }) } pub const fn get_raw(&self) -> u64 { @@ -152,70 +47,9 @@ impl LocalId { } } -impl Debug for LocalId { +impl Debug for UUID { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&*format!("LocalId({:#16X})", self.0)) - } -} - -/// A universally unique identified. -/// -/// A uuid is made up of a global id, local id pair. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub struct UUID { - pub global: GlobalId, - pub local: LocalId, -} - -/// A utility struct providing a simple incrementing counter local id generator. -/// -/// The generator will create its own global id. Local ids will be generated from a incrementing -/// counter. -/// -/// # Examples -/// ``` -/// use rosella_rs::util::id::*; -/// -/// // Create a new generator -/// let generator = IncrementingGenerator::new(); -/// -/// // A new uuid -/// let some_uuid = generator.next().unwrap(); -/// -/// // The global id of the generator will be used for uuids -/// assert_eq!(generator.get_global_id(), some_uuid.global); -/// -/// // Some other uuid -/// let other_uuid = generator.next().unwrap(); -/// assert_ne!(some_uuid, other_uuid); -/// ``` -pub struct IncrementingGenerator { - global: GlobalId, - next: AtomicU64, -} - -impl IncrementingGenerator { - /// Creates a new generator with a new global id and a local id starting at 0. - pub fn new() -> Self { - Self { - global: GlobalId::new(), - next: AtomicU64::new(1), - } - } - - /// Returns the global id of the generator. - pub fn get_global_id(&self) -> GlobalId { - self.global - } - - /// Creates a new uuid - pub fn next(&self) -> Option { - let local = self.next.fetch_add(1u64, std::sync::atomic::Ordering::Relaxed); - - Some(UUID { - global: self.global, - local: LocalId::from_raw(local), - }) + f.write_fmt(format_args!("UUID({:#016X})", self.get_raw())) } } @@ -250,13 +84,10 @@ impl NameType { #[derive(Clone)] pub struct NamedUUID { name: NameType, - id: LocalId, + id: UUID, } impl NamedUUID { - /// The global id used by all NamedUUIDs - pub const GLOBAL_ID: GlobalId = GlobalId::from_raw(1u64); - const fn hash_str_const(name: &str) -> u64 { xxhash_rust::const_xxh3::xxh3_64(name.as_bytes()) } @@ -265,47 +96,52 @@ impl NamedUUID { xxhash_rust::xxh3::xxh3_64(name.as_bytes()) } - pub const fn new_const(name: &'static str) -> NamedUUID { + /// Creates a new uuid based on the hash of the string. Calling this function with the same + /// string will always return the same id. + pub const fn from_str(name: &'static str) -> NamedUUID { let hash = Self::hash_str_const(name); - NamedUUID { name: NameType::new_static(name), id: LocalId::from_hash(hash) } + NamedUUID { name: NameType::new_static(name), id: UUID::from_raw(hash) } } - pub fn new(name: String) -> NamedUUID { + /// Creates a new uuid based on the hash of the string. Calling this function with the same + /// string will always return the same id. + pub fn from_string(name: String) -> NamedUUID { let hash = Self::hash_str(name.as_str()); - NamedUUID { name: NameType::new_string(name), id: LocalId::from_hash(hash) } + NamedUUID { name: NameType::new_string(name), id: UUID::from_raw(hash) } } - pub fn uuid_for(name: &str) -> UUID { - let hash = Self::hash_str(name); + /// Creates a new random uuid with a string attached. Calling this function with the same + /// string will not return the same id. + pub fn with_str(name: &'static str) -> NamedUUID { + NamedUUID { name: NameType::new_static(name), id: UUID::new() } + } - UUID{ global: Self::GLOBAL_ID, local: LocalId::from_hash(hash) } + /// Creates a new random uuid with a string attached. Calling this function with the same + /// string will not return the same id. + pub fn with_string(name: String) -> NamedUUID { + NamedUUID { name: NameType::new_string(name), id: UUID::new() } } - /// Returns the string that generated the UUID + /// Generates the uuid for a string. Does not store the name to allow for parsing non static + /// strings + pub const fn uuid_for(name: &str) -> UUID { + UUID::from_raw(Self::hash_str_const(name)) + } + + /// Returns the attached string pub fn get_name(&self) -> &str { self.name.get() } /// Returns the uuid pub fn get_uuid(&self) -> UUID { - UUID { - global: Self::GLOBAL_ID, - local: self.id, - } - } - - /// Returns the global id - pub fn get_global_id(&self) -> GlobalId { - Self::GLOBAL_ID - } - - /// Returns the local id - pub fn get_local_id(&self) -> LocalId { self.id } + /// Utility function to clone a uuid that has a const string attached to it. + /// This function will panic if the uuid has a non const string attached. pub const fn clone_const(&self) -> Self { match self.name { NameType::String(_) => { @@ -370,52 +206,6 @@ impl Debug for NamedUUID { NameType::Static(str) => *str, NameType::String(str) => str.as_str() }; - f.write_str(&*format!("NamedUUID{{\"{}\", {:?}}}", name, &self.id)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn global_id_uniqueness() { - let id1 = GlobalId::new(); - let id2 = GlobalId::new(); - let id3 = GlobalId::new(); - - assert_ne!(id1, id2); - assert_ne!(id1, id3); - assert_ne!(id2, id3); - } - - #[test] - fn global_id_eq() { - let id1 = GlobalId::new(); - let id2 = GlobalId::new(); - - assert_ne!(id1, id2); - - assert_eq!(id1, id1); - assert_eq!(id2, id2); - - let id1_clone = GlobalId::from_raw(id1.get_raw()); - let id2_clone = GlobalId::from_raw(id2.get_raw()); - - assert_ne!(id1_clone, id2_clone); - assert_ne!(id1, id2_clone); - assert_ne!(id1_clone, id2); - - assert_eq!(id1, id1_clone); - assert_eq!(id2, id2_clone); + f.write_fmt(format_args!("NamedUUID{{\"{}\", {:?}}}", name, &self.id)) } - - /* TODO figure out how to run this without crashing other tests - #[test] - #[should_panic] - fn global_id_overflow() { - NEXT_GLOBAL_ID.store(u64::MAX - 10u64, std::sync::atomic::Ordering::SeqCst); - - GlobalId::new(); - }*/ } \ No newline at end of file diff --git a/src/util/mod.rs b/src/util/mod.rs index f8b5082..685c28b 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,6 +1,7 @@ pub mod id; pub mod extensions; pub mod slice_splitter; +pub mod rand; -#[cfg(test)] +#[cfg(any(test, feature = "__internal_doc_test"))] pub mod test; diff --git a/src/util/rand.rs b/src/util/rand.rs new file mode 100644 index 0000000..50a2b3c --- /dev/null +++ b/src/util/rand.rs @@ -0,0 +1,84 @@ +/// Implements the Xoshiro256++ random number algorithm. +/// See https://prng.di.unimi.it/xoshiro256plusplus.c +#[derive(Copy, Clone)] +pub struct Xoshiro256PlusPlus { + s: [u64; 4], +} + +impl Xoshiro256PlusPlus { + const JUMP : [u64; 4] = [0x180ec6d33cfd0abau64, 0xd5a61266f0c9392cu64, 0xa9582618e03fc9aau64, 0x39abdc4529b1661cu64]; + const LONG_JUMP : [u64; 4] = [ 0x76e15d3efefdcbbfu64, 0xc5004e441c522fb3u64, 0x77710069854ee241u64, 0x39109bb02acbe635u64 ]; + + /// Creates a new random number generator with specified seed + pub const fn from_seed(seed: [u64; 4]) -> Self { + Self{ s: seed } + } + + const fn rotl(x: u64, k: i32) -> u64 { + (x << k) | (x >> (64i32 - k)) + } + + /// Generates a new random number + pub fn gen(&mut self) -> u64 { + let result = u64::overflowing_add(Self::rotl(u64::overflowing_add(self.s[0], self.s[3]).0, 23), self.s[0]).0; + + let t = self.s[1] << 17; + + self.s[2] ^= self.s[0]; + self.s[3] ^= self.s[1]; + self.s[1] ^= self.s[2]; + self.s[0] ^= self.s[3]; + + self.s[2] ^= t; + + self.s[3] = Self::rotl(self.s[3], 45); + + result + } + + /// Utility function to create the jump and long_jump functions + fn update_with(&mut self, update: [u64; SIZE]) { + let mut s0 = 0u64; + let mut s1 = 0u64; + let mut s2 = 0u64; + let mut s3 = 0u64; + + for i in 0..update.len() { + for b in 0..64u32 { + if (update[i] & (1u64 << b)) != 0u64 { + s0 ^= self.s[0]; + s1 ^= self.s[1]; + s2 ^= self.s[2]; + s3 ^= self.s[3]; + } + self.gen(); + } + } + + self.s[0] = s0; + self.s[1] = s1; + self.s[2] = s2; + self.s[3] = s3; + } + + /// This function is equivalent to 2^128 calls to [`Self::gen`]. It can be used to generate + /// 2^128 non-overlapping subsequences for parallel computations. + pub fn jump(&mut self) { + self.update_with(Self::JUMP) + } + + /// This function is equivalent to 2^192 calls to [`Self::gen`]. It can be used to generate + /// 2^64 starting points from each of which [`Self::jump`] will generate 2^64 non-overlapping + /// subsequences for parallel distributed computations. + pub fn long_jump(&mut self) { + self.update_with(Self::LONG_JUMP) + } +} + +impl Iterator for Xoshiro256PlusPlus { + type Item = u64; + + fn next(&mut self) -> Option { + Some(self.gen()) + } +} \ No newline at end of file diff --git a/src/util/test.rs b/src/util/test.rs index 415fa00..cd70749 100644 --- a/src/util/test.rs +++ b/src/util/test.rs @@ -20,7 +20,7 @@ pub fn make_headless_instance_device() -> (InstanceContext, DeviceContext) { register_rosella_debug(&mut registry, false); let instance = create_instance(&mut registry, "RosellaUnitTests", 1).unwrap(); - let device = create_device(&mut registry, instance.clone()).unwrap(); + let device = create_device(&mut registry, instance.clone(), &[]).unwrap(); (instance, device) } \ No newline at end of file diff --git a/src/window.rs b/src/window.rs index 3431d82..3a01374 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,14 +1,16 @@ -use ash::extensions::khr::Surface; use ash::vk::SurfaceKHR; -use ash::{Entry, Instance}; +use ash::vk; use winit::dpi::LogicalSize; use winit::event_loop::EventLoop; use winit::window::WindowBuilder; +use crate::objects::surface::SurfaceProvider; + +use crate::rosella::InstanceContext; /// Represents a ash surface and a KHR surface pub struct RosellaSurface { - pub ash_surface: Surface, - pub khr_surface: SurfaceKHR, + instance: InstanceContext, + surface: vk::SurfaceKHR, } pub struct RosellaWindow { @@ -17,15 +19,30 @@ pub struct RosellaWindow { } impl RosellaSurface { - pub fn new(instance: &Instance, vk: &Entry, window: &RosellaWindow) -> Self { + pub fn new(instance: &InstanceContext, window: &RosellaWindow) -> Self { + let surface = unsafe { + ash_window::create_surface(instance.get_entry(), instance.vk(), &window.handle, None) + }.unwrap(); + RosellaSurface { - ash_surface: Surface::new(vk, instance), - khr_surface: unsafe { ash_window::create_surface(vk, instance, &window.handle, None) } - .expect("Failed to create window surface."), + instance: instance.clone(), + surface, } } } +impl SurfaceProvider for RosellaSurface { + fn get_handle(&self) -> SurfaceKHR { + self.surface + } +} + +impl Drop for RosellaSurface { + fn drop(&mut self) { + unsafe { self.instance.get_extension::().unwrap().destroy_surface(self.surface, None) } + } +} + impl RosellaWindow { pub fn new(title: &str, width: f64, height: f64) -> RosellaWindow { let event_loop = EventLoop::new(); diff --git a/tests/headless_init.rs b/tests/headless_init.rs index 9999043..946c65e 100644 --- a/tests/headless_init.rs +++ b/tests/headless_init.rs @@ -16,7 +16,7 @@ fn init_no_feature() { }; #[allow(unused)] - let device_context = match create_device(&mut registry, instance_context.clone()) { + let device_context = match create_device(&mut registry, instance_context.clone(), &[]) { Ok(res) => res, Err(err) => { panic!("Failed to create device {:?}", err); @@ -37,7 +37,7 @@ fn init_rosella() { }; #[allow(unused)] - let device_context = match create_device(&mut registry, instance_context.clone()) { + let device_context = match create_device(&mut registry, instance_context.clone(), &[]) { Ok(res) => res, Err(err) => { panic!("Failed to create device {:?}", err);