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::<HashSet<_>>();
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;
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<Self> {
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."
)))
}
}
}