use std::collections::HashSet; use log::{info, warn}; use thiserror::Error; use anyhow::{anyhow, Result}; use vulkanalia::prelude::v1_0::*; use vulkanalia::vk::KhrSurfaceExtension; use vulkanalia::Instance; use crate::app::AppData; use crate::msaa::get_max_msaa_samples; use crate::swapchain::SwapchainSupport; pub(crate) const DEVICE_EXTENSIONS: &[vk::ExtensionName] = &[vk::KHR_SWAPCHAIN_EXTENSION.name]; #[derive(Debug, Error)] #[error("Missing {0}.")] pub(crate) struct SuitabilityError(pub(crate) &'static str); unsafe fn check_physical_device_extensions( instance: &Instance, physical_device: vk::PhysicalDevice, ) -> Result<()> { let extensions = instance .enumerate_device_extension_properties(physical_device, None)? .iter() .map(|e| e.extension_name) .collect::>(); if DEVICE_EXTENSIONS.iter().all(|e| extensions.contains(e)) { Ok(()) } else { Err(anyhow!(SuitabilityError( "Missing required device extensions." ))) } } unsafe fn check_physical_device( instance: &Instance, data: &AppData, physical_device: vk::PhysicalDevice, ) -> Result<()> { let properties = instance.get_physical_device_properties(physical_device); if properties.device_type != vk::PhysicalDeviceType::DISCRETE_GPU { return Err(anyhow!(SuitabilityError( "Only discrete GPUs are supported." ))); } let features = instance.get_physical_device_features(physical_device); if features.sampler_anisotropy != vk::TRUE { return Err(anyhow!(SuitabilityError("No sampler anisotropy."))); } if features.geometry_shader != vk::TRUE { return Err(anyhow!(SuitabilityError( "Missing geometry shader support." ))); } QueueFamilyIndices::get(instance, data, physical_device)?; check_physical_device_extensions(instance, physical_device)?; let support = SwapchainSupport::get(instance, data, physical_device)?; if support.formats.is_empty() || support.present_modes.is_empty() { return Err(anyhow!(SuitabilityError("Insufficient swapchain support."))); } Ok(()) } pub(crate) unsafe fn pick_physical_device(instance: &Instance, data: &mut AppData) -> Result<()> { for physical_device in instance.enumerate_physical_devices()? { let properties = instance.get_physical_device_properties(physical_device); if let Err(error) = check_physical_device(instance, data, physical_device) { warn!( "Skipping physical device (`{}`): {}", properties.device_name, error ); } else { info!("Selected physical device (`{}`).", properties.device_name); data.physical_device = physical_device; data.msaa_samples = get_max_msaa_samples(instance, data); data.limit_max_sampler_anisotropy = properties.limits.max_sampler_anisotropy; data.limit_max_push_constants_size = properties.limits.max_push_constants_size; // TODO settings file data.setting_anisotropy = true; data.setting_max_sampler_anisotropy = data.limit_max_sampler_anisotropy; data.setting_sample_shading = true; return Ok(()); } } Err(anyhow!("Failed to find suitable physical device.")) } #[derive(Copy, Clone, Debug)] pub(crate) struct QueueFamilyIndices { pub(crate) graphics: u32, pub(crate) present: u32, } impl QueueFamilyIndices { pub(crate) unsafe fn get( instance: &Instance, data: &AppData, physical_device: vk::PhysicalDevice, ) -> Result { let properties = instance.get_physical_device_queue_family_properties(physical_device); let mut present = None; for (index, _properties) in properties.iter().enumerate() { if instance.get_physical_device_surface_support_khr( physical_device, index as u32, data.surface, )? { present = Some(index as u32); break; } } let graphics = properties .iter() .position(|p| p.queue_flags.contains(vk::QueueFlags::GRAPHICS)) .map(|i| i as u32); if let (Some(graphics), Some(present)) = (graphics, present) { Ok(Self { graphics, present }) } else { Err(anyhow!(SuitabilityError( "Missing required queue families." ))) } } }