Skip to main content

ferroui_vulkan/
vulkan_options.rs

1use crate::{IVulkanDevice, IVulkanInstance, IVulkanKhrSurfacePlatformSurfaceFactory, VulkanException};
2use std::any::{Any, TypeId};
3use std::collections::HashMap;
4use std::fmt;
5use std::sync::Arc;
6
7/// The address of an instance-level entry point of Vulkan by its name
8/// (`vkGetInstanceProcAddr`): the instance, zero for the entry points that
9/// need none, and the name; zero when there is no such entry point.
10pub type VkGetInstanceProcAddressDelegate = Arc<dyn Fn(isize, &str) -> isize + Send + Sync>;
11
12/// Creates a `VkSurfaceKHR` on an instance, for asking the physical devices
13/// whether they can present. Zero stands for no surface: the devices are
14/// not asked then. A failure is the failure of the creation of the device.
15pub type VulkanDeviceCheckSurfaceFactory =
16    Arc<dyn Fn(&Arc<dyn IVulkanInstance>) -> Result<u64, VulkanException> + Send + Sync>;
17
18/// A version number of three parts.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct Version {
21    pub major: i32,
22    pub minor: i32,
23    pub build: i32,
24}
25
26impl Version {
27    pub const fn new(major: i32, minor: i32, build: i32) -> Self {
28        Self { major, minor, build }
29    }
30}
31
32impl fmt::Display for Version {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        write!(f, "{}.{}.{}", self.major, self.minor, self.build)
35    }
36}
37
38/// How an application wants Vulkan to be set up.
39#[derive(Clone, Default)]
40pub struct VulkanOptions {
41    pub vulkan_instance_creation_options: VulkanInstanceCreationOptions,
42    pub vulkan_device_creation_options: VulkanDeviceCreationOptions,
43    /// A device of the application that every context renders with, in
44    /// place of an instance and devices the platform graphics create.
45    pub custom_shared_device: Option<Arc<dyn IVulkanDevice>>,
46}
47
48#[derive(Clone)]
49pub struct VulkanInstanceCreationOptions {
50    /// Replaces the way the platform finds the entry points of Vulkan.
51    pub custom_get_proc_address_delegate: Option<VkGetInstanceProcAddressDelegate>,
52
53    /// Sets the application name of the vulkan instance
54    pub application_name: Option<String>,
55
56    /// Specifies the vulkan api version to use
57    pub vulkan_version: Version,
58
59    /// Specifies additional extensions to enable if available on the instance
60    pub instance_extensions: Vec<String>,
61
62    /// Specifies layers to enable if available on the instance
63    pub enabled_layers: Vec<String>,
64
65    /// Enables the debug layer
66    pub use_debug: bool,
67
68    /// Requires and enables VK_KHR_surface on the created instance. Disable for offscreen-only
69    /// rendering with drivers that don't expose the extension.
70    pub require_surface_extension: bool,
71}
72
73impl Default for VulkanInstanceCreationOptions {
74    fn default() -> Self {
75        Self {
76            custom_get_proc_address_delegate: None,
77            application_name: None,
78            vulkan_version: Version::new(1, 1, 0),
79            instance_extensions: Vec::new(),
80            enabled_layers: Vec::new(),
81            use_debug: false,
82            require_surface_extension: true,
83        }
84    }
85}
86
87#[derive(Clone, Debug, PartialEq, Eq)]
88pub struct VulkanDeviceCreationOptions {
89    /// Specifies extensions to enable if available on the logical device
90    pub device_extensions: Vec<String>,
91
92    /// Selects the first suitable discrete gpu available
93    pub prefer_discrete_gpu: bool,
94
95    pub require_compute_bit: bool,
96
97    /// Requires and enables VK_KHR_swapchain on the created device. Disable for offscreen-only
98    /// rendering with drivers that don't expose the extension.
99    pub require_swapchain_extension: bool,
100}
101
102impl Default for VulkanDeviceCreationOptions {
103    fn default() -> Self {
104        Self {
105            device_extensions: Vec::new(),
106            prefer_discrete_gpu: false,
107            require_compute_bit: false,
108            require_swapchain_extension: true,
109        }
110    }
111}
112
113/// What a windowing platform adds to the options of the application.
114#[derive(Clone, Default)]
115pub struct VulkanPlatformSpecificOptions {
116    /// The instance extensions the surfaces of the platform need.
117    pub required_instance_extensions: Vec<String>,
118
119    /// How the platform finds the entry points of Vulkan.
120    pub get_proc_address_delegate: Option<VkGetInstanceProcAddressDelegate>,
121
122    /// Creates a surface the physical devices are asked about: a device
123    /// whose queues cannot present to it is not taken.
124    pub device_check_surface_factory: Option<VulkanDeviceCheckSurfaceFactory>,
125
126    /// The features every context of the platform has, by the type that
127    /// names the feature. A value holds an `Arc` of that type (for
128    /// `TypeId::of::<dyn IFoo>()` an `Arc<dyn IFoo>`).
129    pub platform_features: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
130}
131
132impl VulkanPlatformSpecificOptions {
133    /// Registers the factory of the surfaces of the platform among the
134    /// platform features.
135    pub fn set_khr_surface_factory(&mut self, factory: Arc<dyn IVulkanKhrSurfacePlatformSurfaceFactory>) {
136        self.platform_features
137            .insert(TypeId::of::<dyn IVulkanKhrSurfacePlatformSurfaceFactory>(), Arc::new(factory));
138    }
139
140    /// The factory of the surfaces of the platform, when one is registered.
141    pub fn khr_surface_factory(&self) -> Option<Arc<dyn IVulkanKhrSurfacePlatformSurfaceFactory>> {
142        khr_surface_factory_of(&self.platform_features)
143    }
144}
145
146/// The factory of the surfaces of a platform among its platform features.
147pub(crate) fn khr_surface_factory_of(
148    platform_features: &HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
149) -> Option<Arc<dyn IVulkanKhrSurfacePlatformSurfaceFactory>> {
150    platform_features
151        .get(&TypeId::of::<dyn IVulkanKhrSurfacePlatformSurfaceFactory>())?
152        .downcast_ref::<Arc<dyn IVulkanKhrSurfacePlatformSurfaceFactory>>()
153        .cloned()
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use crate::{IVulkanKhrSurfacePlatformSurface, IVulkanPlatformGraphicsContext};
160    use ferroui_base::platform::surfaces::IPlatformRenderSurface;
161    use std::rc::Rc;
162
163    #[test]
164    fn the_defaults_are_those_of_the_reference() {
165        let options = VulkanOptions::default();
166        let instance = &options.vulkan_instance_creation_options;
167        assert!(instance.custom_get_proc_address_delegate.is_none());
168        assert_eq!(instance.application_name, None);
169        assert_eq!(instance.vulkan_version, Version::new(1, 1, 0));
170        assert!(instance.instance_extensions.is_empty());
171        assert!(instance.enabled_layers.is_empty());
172        assert!(!instance.use_debug);
173        assert!(instance.require_surface_extension);
174
175        let device = &options.vulkan_device_creation_options;
176        assert!(device.device_extensions.is_empty());
177        assert!(!device.prefer_discrete_gpu);
178        assert!(!device.require_compute_bit);
179        assert!(device.require_swapchain_extension);
180
181        assert!(options.custom_shared_device.is_none());
182
183        let platform = VulkanPlatformSpecificOptions::default();
184        assert!(platform.required_instance_extensions.is_empty());
185        assert!(platform.get_proc_address_delegate.is_none());
186        assert!(platform.device_check_surface_factory.is_none());
187        assert!(platform.platform_features.is_empty());
188        assert!(platform.khr_surface_factory().is_none());
189    }
190
191    #[test]
192    fn a_version_prints_its_three_parts() {
193        assert_eq!(Version::new(1, 1, 0).to_string(), "1.1.0");
194    }
195
196    struct Factory;
197
198    impl IVulkanKhrSurfacePlatformSurfaceFactory for Factory {
199        fn can_render_to_surface(
200            &self,
201            _context: &dyn IVulkanPlatformGraphicsContext,
202            _surface: &Arc<dyn IPlatformRenderSurface>,
203        ) -> bool {
204            false
205        }
206
207        fn create_surface(
208            &self,
209            _context: &dyn IVulkanPlatformGraphicsContext,
210            _surface: &Arc<dyn IPlatformRenderSurface>,
211        ) -> Rc<dyn IVulkanKhrSurfacePlatformSurface> {
212            unreachable!()
213        }
214    }
215
216    #[test]
217    fn the_surface_factory_is_a_platform_feature() {
218        let mut platform = VulkanPlatformSpecificOptions::default();
219        let factory: Arc<dyn IVulkanKhrSurfacePlatformSurfaceFactory> = Arc::new(Factory);
220        platform.set_khr_surface_factory(factory.clone());
221
222        assert_eq!(platform.platform_features.len(), 1);
223        let found = platform.khr_surface_factory().expect("the factory is registered");
224        assert!(Arc::ptr_eq(&found, &factory));
225
226        // The options are copied into the platform graphics: the copy has the feature too.
227        let copy = platform.clone();
228        assert!(Arc::ptr_eq(&copy.khr_surface_factory().unwrap(), &factory));
229    }
230}