Add logical device struct and surface handling for Vulkan
Some checks failed
Build legacy Nix package on Ubuntu / build (push) Failing after 0s

Introduce the VkLogicalDevice struct and add surface creation logic in VkInstance. Also, import necessary extensions and refine Vulkan physical device and window handling. Included a dependency on 'anyhow' for error management.
This commit is contained in:
Florian RICHER 2024-11-10 18:18:59 +01:00
parent 4048937a6c
commit da0be47b14
Signed by: florian.richer
GPG key ID: C73D37CBED7BFC77
14 changed files with 258 additions and 153 deletions

View file

@ -1,6 +1,8 @@
pub(self) mod vk_instance;
pub(self) mod vk_physical_device;
pub(self) mod vk_logical_device;
mod utils;
mod vk_surface;
pub use vk_instance::VkInstance;
pub use vk_physical_device::VkPhysicalDevice;

View file

@ -1,74 +0,0 @@
use std::ffi::CString;
pub fn use_layers(
entry: &ash::Entry,
layers_to_select: Vec<&str>,
) -> Vec<CString> {
let layers_available = get_layers_available(entry);
log_layers_available(&layers_available);
let selected_layers = select_layers(&layers_available, &layers_to_select);
log_layers_wanted(&layers_to_select, &selected_layers);
selected_layers
.iter()
.map(|sl| CString::new(sl.clone().as_bytes()).unwrap())
.collect::<Vec<_>>()
}
fn get_layers_available(entry: &ash::Entry) -> Vec<ash::vk::LayerProperties> {
unsafe { entry.enumerate_instance_layer_properties().unwrap_or_default() }
}
fn log_layers_available(layers_available: &[ash::vk::LayerProperties]) {
log::info!("Available layers ({}):", layers_available.len());
for l in layers_available {
log::info!(
"\t{:?}\tImplementation version: {}\tVulkan Version: {}\tDescription: {:?}",
l.layer_name_as_c_str().unwrap_or_default(),
l.implementation_version,
print_version(l.spec_version),
l.description_as_c_str().unwrap_or_default()
);
}
log::info!(""); // Add blank line
}
fn select_layers(
layers_available: &[ash::vk::LayerProperties],
layers_to_select: &[&str],
) -> Vec<String> {
layers_available
.iter()
.filter_map(|l| {
let layer_name = l
.layer_name_as_c_str()
.unwrap_or_default()
.to_string_lossy();
layers_to_select
.iter()
.find(|&&ln| ln == layer_name)
.map(|_| layer_name.into_owned())
})
.collect()
}
fn log_layers_wanted(layers_to_select: &[&str], selected_layers: &[String]) {
log::info!("Layers wanted ({}):", layers_to_select.len());
for ol in layers_to_select {
let selected = selected_layers.iter().any(|sl| sl == ol);
log::info!("\t{:?}\tSelected: {}", ol, selected);
}
log::info!(""); // Add blank line
}
pub fn print_version(version: u32) -> String {
format!(
"{}.{}.{}",
ash::vk::api_version_major(version),
ash::vk::api_version_minor(version),
ash::vk::api_version_patch(version)
)
}

View file

@ -0,0 +1,38 @@
use std::ffi::CString;
pub fn use_layers(
entry: &ash::Entry,
layers_to_select: Vec<&str>,
) -> Vec<CString> {
let layers_available = get_layers_available(entry);
let selected_layers = select_layers(&layers_available, &layers_to_select);
selected_layers
.iter()
.map(|sl| CString::new(sl.clone().as_bytes()).unwrap())
.collect::<Vec<_>>()
}
fn get_layers_available(entry: &ash::Entry) -> Vec<ash::vk::LayerProperties> {
unsafe { entry.enumerate_instance_layer_properties().unwrap_or_default() }
}
fn select_layers(
layers_available: &[ash::vk::LayerProperties],
layers_to_select: &[&str],
) -> Vec<String> {
layers_available
.iter()
.filter_map(|l| {
let layer_name = l
.layer_name_as_c_str()
.unwrap_or_default()
.to_string_lossy();
layers_to_select
.iter()
.find(|&&ln| ln == layer_name)
.map(|_| layer_name.into_owned())
})
.collect()
}

11
src/vulkan/utils/mod.rs Normal file
View file

@ -0,0 +1,11 @@
mod layers;
pub use layers::use_layers;
pub fn print_version(version: u32) -> String {
format!(
"{}.{}.{}",
ash::vk::api_version_major(version),
ash::vk::api_version_minor(version),
ash::vk::api_version_patch(version)
)
}

View file

@ -1,7 +1,10 @@
use std::ffi::CString;
use std::ffi::{c_char, CString};
use std::fmt::{Display, Formatter};
use ash::{Instance, vk, Entry};
use winit::raw_window_handle::{HasDisplayHandle};
use ash::khr::surface;
use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use crate::vulkan::utils::use_layers;
use crate::vulkan::vk_surface::VkSurface;
use crate::vulkan::VkPhysicalDevice;
pub struct VkInstance {
@ -10,7 +13,9 @@ pub struct VkInstance {
}
impl VkInstance {
pub fn new(window: &impl HasDisplayHandle) -> Self {
pub fn new(
required_extensions: &Vec<*const c_char>,
) -> Self {
let entry = Entry::linked();
// Layers
@ -21,19 +26,6 @@ impl VkInstance {
]);
let layers_raw = layers.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
// Extensions
let mut extension_names =
ash_window::enumerate_required_extensions(window.display_handle().expect("No display handle").as_raw())
.unwrap()
.to_vec();
#[cfg(any(target_os = "macos", target_os = "ios"))]
{
extension_names.push(ash::khr::portability_enumeration::NAME.as_ptr());
// Enabling this extension is a requirement when using `VK_KHR_portability_subset`
extension_names.push(ash::khr::get_physical_device_properties2::NAME.as_ptr());
}
// App Info
let app_name = CString::new("VulkanTriangle").unwrap();
let appinfo = vk::ApplicationInfo::default()
@ -53,7 +45,7 @@ impl VkInstance {
let create_info = vk::InstanceCreateInfo::default()
.application_info(&appinfo)
.enabled_layer_names(&layers_raw)
.enabled_extension_names(&extension_names)
.enabled_extension_names(&required_extensions)
.flags(create_flags);
let instance: Instance = unsafe {
@ -75,6 +67,31 @@ impl VkInstance {
.iter().map(|physical_device| VkPhysicalDevice::new(&self.handle, *physical_device))
.collect()
}
pub fn create_surface(
&self,
window: &crate::display::Window
) -> anyhow::Result<VkSurface> {
let window_handle = window.handle()
.ok_or_else(|| anyhow::anyhow!("Window handle is not available."))?;
let surface_loader = surface::Instance::new(&self.entry, &self.handle);
let surface = unsafe {
ash_window::create_surface(
&self.entry,
&self.handle,
window_handle.display_handle()?.as_raw(),
window_handle.window_handle()?.as_raw(),
None,
)?
};
Ok(VkSurface::new(
surface_loader,
surface,
))
}
}
impl Drop for VkInstance {
@ -83,4 +100,10 @@ impl Drop for VkInstance {
self.handle.destroy_instance(None);
}
}
}
impl Display for VkInstance {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Vulkan Version:")
}
}

View file

@ -0,0 +1,3 @@
pub struct VkLogicalDevice {
}

View file

@ -1,10 +1,11 @@
use std::cmp::Ordering;
use std::fmt::{Display, Formatter};
use ash::vk;
use crate::display::App;
use crate::vulkan::vk_surface::VkSurface;
pub struct VkPhysicalDevice {
// Vulkan properties
handle: vk::PhysicalDevice,
pub(super) handle: vk::PhysicalDevice,
pub properties: vk::PhysicalDeviceProperties,
pub features: vk::PhysicalDeviceFeatures,
pub queue_family_properties: Vec<vk::QueueFamilyProperties>,
@ -26,33 +27,16 @@ impl VkPhysicalDevice {
}
pub fn priority(&self) -> usize {
let mut priority = 0;
let has_graphics_support = self.queue_family_properties.iter().any(|qf| qf.queue_flags.contains(vk::QueueFlags::GRAPHICS));
let has_compute_support = self.queue_family_properties.iter().any(|qf| qf.queue_flags.contains(vk::QueueFlags::COMPUTE));
let has_transfer_support = self.queue_family_properties.iter().any(|qf| qf.queue_flags.contains(vk::QueueFlags::TRANSFER));
let has_sparse_binding_support = self.queue_family_properties.iter().any(|qf| qf.queue_flags.contains(vk::QueueFlags::SPARSE_BINDING));
let physical_device_type = self.properties.device_type;
priority |= has_graphics_support as usize;
priority |= (has_sparse_binding_support as usize) << 1;
priority |= (has_transfer_support as usize) << 2;
priority |= (has_compute_support as usize) << 3;
let weight : usize = match physical_device_type {
match self.properties.device_type {
vk::PhysicalDeviceType::CPU => 1,
vk::PhysicalDeviceType::VIRTUAL_GPU => 2,
vk::PhysicalDeviceType::INTEGRATED_GPU => 3,
vk::PhysicalDeviceType::DISCRETE_GPU => 4,
_ => 0
};
priority |= weight << 4;
priority
}
}
}
impl Display for VkPhysicalDevice {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "\tNom: {:?}, Priorité: {}", self.properties.device_name_as_c_str().unwrap_or_default(), self.priority())

29
src/vulkan/vk_surface.rs Normal file
View file

@ -0,0 +1,29 @@
use ash::prelude::VkResult;
use crate::vulkan::VkPhysicalDevice;
pub struct VkSurface {
surface_loader: ash::khr::surface::Instance,
surface: ash::vk::SurfaceKHR,
}
impl VkSurface {
pub fn new(
surface_loader: ash::khr::surface::Instance,
surface: ash::vk::SurfaceKHR,
) -> Self {
Self {
surface_loader,
surface
}
}
pub fn physical_device_queue_supported(&self, physical_device: &VkPhysicalDevice, queue_index: u32) -> VkResult<bool> {
unsafe {
self.surface_loader.get_physical_device_surface_support(
physical_device.handle,
queue_index,
self.surface
)
}
}
}