use crate::{IVulkanDevice, IVulkanInstance, IVulkanKhrSurfacePlatformSurfaceFactory, VulkanException};
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
pub type VkGetInstanceProcAddressDelegate = Arc<dyn Fn(isize, &str) -> isize + Send + Sync>;
pub type VulkanDeviceCheckSurfaceFactory =
Arc<dyn Fn(&Arc<dyn IVulkanInstance>) -> Result<u64, VulkanException> + Send + Sync>;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Version {
pub major: i32,
pub minor: i32,
pub build: i32,
}
impl Version {
pub const fn new(major: i32, minor: i32, build: i32) -> Self {
Self { major, minor, build }
}
}
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.build)
}
}
#[derive(Clone, Default)]
pub struct VulkanOptions {
pub vulkan_instance_creation_options: VulkanInstanceCreationOptions,
pub vulkan_device_creation_options: VulkanDeviceCreationOptions,
pub custom_shared_device: Option<Arc<dyn IVulkanDevice>>,
}
#[derive(Clone)]
pub struct VulkanInstanceCreationOptions {
pub custom_get_proc_address_delegate: Option<VkGetInstanceProcAddressDelegate>,
pub application_name: Option<String>,
pub vulkan_version: Version,
pub instance_extensions: Vec<String>,
pub enabled_layers: Vec<String>,
pub use_debug: bool,
pub require_surface_extension: bool,
}
impl Default for VulkanInstanceCreationOptions {
fn default() -> Self {
Self {
custom_get_proc_address_delegate: None,
application_name: None,
vulkan_version: Version::new(1, 1, 0),
instance_extensions: Vec::new(),
enabled_layers: Vec::new(),
use_debug: false,
require_surface_extension: true,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VulkanDeviceCreationOptions {
pub device_extensions: Vec<String>,
pub prefer_discrete_gpu: bool,
pub require_compute_bit: bool,
pub require_swapchain_extension: bool,
}
impl Default for VulkanDeviceCreationOptions {
fn default() -> Self {
Self {
device_extensions: Vec::new(),
prefer_discrete_gpu: false,
require_compute_bit: false,
require_swapchain_extension: true,
}
}
}
#[derive(Clone, Default)]
pub struct VulkanPlatformSpecificOptions {
pub required_instance_extensions: Vec<String>,
pub get_proc_address_delegate: Option<VkGetInstanceProcAddressDelegate>,
pub device_check_surface_factory: Option<VulkanDeviceCheckSurfaceFactory>,
pub platform_features: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
}
impl VulkanPlatformSpecificOptions {
pub fn set_khr_surface_factory(&mut self, factory: Arc<dyn IVulkanKhrSurfacePlatformSurfaceFactory>) {
self.platform_features
.insert(TypeId::of::<dyn IVulkanKhrSurfacePlatformSurfaceFactory>(), Arc::new(factory));
}
pub fn khr_surface_factory(&self) -> Option<Arc<dyn IVulkanKhrSurfacePlatformSurfaceFactory>> {
khr_surface_factory_of(&self.platform_features)
}
}
pub(crate) fn khr_surface_factory_of(
platform_features: &HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
) -> Option<Arc<dyn IVulkanKhrSurfacePlatformSurfaceFactory>> {
platform_features
.get(&TypeId::of::<dyn IVulkanKhrSurfacePlatformSurfaceFactory>())?
.downcast_ref::<Arc<dyn IVulkanKhrSurfacePlatformSurfaceFactory>>()
.cloned()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{IVulkanKhrSurfacePlatformSurface, IVulkanPlatformGraphicsContext};
use ferroui_base::platform::surfaces::IPlatformRenderSurface;
use std::rc::Rc;
#[test]
fn the_defaults_are_those_of_the_reference() {
let options = VulkanOptions::default();
let instance = &options.vulkan_instance_creation_options;
assert!(instance.custom_get_proc_address_delegate.is_none());
assert_eq!(instance.application_name, None);
assert_eq!(instance.vulkan_version, Version::new(1, 1, 0));
assert!(instance.instance_extensions.is_empty());
assert!(instance.enabled_layers.is_empty());
assert!(!instance.use_debug);
assert!(instance.require_surface_extension);
let device = &options.vulkan_device_creation_options;
assert!(device.device_extensions.is_empty());
assert!(!device.prefer_discrete_gpu);
assert!(!device.require_compute_bit);
assert!(device.require_swapchain_extension);
assert!(options.custom_shared_device.is_none());
let platform = VulkanPlatformSpecificOptions::default();
assert!(platform.required_instance_extensions.is_empty());
assert!(platform.get_proc_address_delegate.is_none());
assert!(platform.device_check_surface_factory.is_none());
assert!(platform.platform_features.is_empty());
assert!(platform.khr_surface_factory().is_none());
}
#[test]
fn a_version_prints_its_three_parts() {
assert_eq!(Version::new(1, 1, 0).to_string(), "1.1.0");
}
struct Factory;
impl IVulkanKhrSurfacePlatformSurfaceFactory for Factory {
fn can_render_to_surface(
&self,
_context: &dyn IVulkanPlatformGraphicsContext,
_surface: &Arc<dyn IPlatformRenderSurface>,
) -> bool {
false
}
fn create_surface(
&self,
_context: &dyn IVulkanPlatformGraphicsContext,
_surface: &Arc<dyn IPlatformRenderSurface>,
) -> Rc<dyn IVulkanKhrSurfacePlatformSurface> {
unreachable!()
}
}
#[test]
fn the_surface_factory_is_a_platform_feature() {
let mut platform = VulkanPlatformSpecificOptions::default();
let factory: Arc<dyn IVulkanKhrSurfacePlatformSurfaceFactory> = Arc::new(Factory);
platform.set_khr_surface_factory(factory.clone());
assert_eq!(platform.platform_features.len(), 1);
let found = platform.khr_surface_factory().expect("the factory is registered");
assert!(Arc::ptr_eq(&found, &factory));
let copy = platform.clone();
assert!(Arc::ptr_eq(©.khr_surface_factory().unwrap(), &factory));
}
}