Add logical device struct and surface handling for Vulkan
Some checks failed
Build legacy Nix package on Ubuntu / build (push) Failing after 0s
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:
parent
4048937a6c
commit
da0be47b14
14 changed files with 258 additions and 153 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -116,6 +116,12 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.9"
|
||||
|
@ -1066,6 +1072,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
|||
name = "rust_ash_test"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ash",
|
||||
"ash-window",
|
||||
"env_logger",
|
||||
|
|
|
@ -6,6 +6,7 @@ authors = ["Florian RICHER <florian.richer@protonmail.com>"]
|
|||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
winit = { version = "0.30", features = [ "rwh_06" ] }
|
||||
ash = { version = "0.38", default-features = false, features = ["linked", "debug", "std"] }
|
||||
ash-window = "0.13"
|
||||
|
|
|
@ -1,52 +1,70 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
use winit::{
|
||||
application::ApplicationHandler, event::WindowEvent, event_loop::ActiveEventLoop, raw_window_handle::{HasDisplayHandle, DisplayHandle, HandleError}, window::{Window, WindowId}
|
||||
};
|
||||
use winit::window::WindowAttributes;
|
||||
use crate::vulkan::VkInstance;
|
||||
use ash::vk::QueueFlags;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
use winit::window::{WindowId};
|
||||
use crate::display::window::Window;
|
||||
use crate::vulkan::{VkInstance, VkPhysicalDevice};
|
||||
|
||||
pub struct App {
|
||||
window_attributes: WindowAttributes,
|
||||
window: Option<Window>,
|
||||
instance: Option<VkInstance>,
|
||||
window: Window,
|
||||
instance: Option<VkInstance>
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(window_attributes: WindowAttributes) -> Self {
|
||||
pub fn new(window: Window) -> Self {
|
||||
Self {
|
||||
window_attributes,
|
||||
window: None,
|
||||
instance: None,
|
||||
window,
|
||||
instance: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasDisplayHandle for App {
|
||||
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
|
||||
self.window.as_ref()
|
||||
.ok_or_else(|| HandleError::Unavailable)?
|
||||
.display_handle()
|
||||
fn init_vulkan(&mut self) {
|
||||
let required_extensions = self.window
|
||||
.required_extensions()
|
||||
.expect("Unable to get required required extensions");
|
||||
|
||||
log::info!("Initializing Vulkan instance");
|
||||
let instance = VkInstance::new(&required_extensions);
|
||||
log::info!("Vulkan instance created");
|
||||
log::info!("\t{}", instance);
|
||||
|
||||
let surface = instance.create_surface(&self.window)
|
||||
.expect("Unable to create surface");
|
||||
|
||||
let mut physical_devices = instance.get_physical_devices();
|
||||
physical_devices.sort_by(|a, b| b.priority().cmp(&a.priority()));
|
||||
|
||||
let (physical_device, queue_family_index) = physical_devices
|
||||
.iter()
|
||||
.find_map(|physical_device| {
|
||||
physical_device.queue_family_properties
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(index, queue_family_property)| {
|
||||
if surface.physical_device_queue_supported(physical_device, index as u32).unwrap_or(false) {
|
||||
Some((physical_device, index as u32))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.expect("Unable to find suitable device");
|
||||
|
||||
|
||||
self.instance = Some(instance);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
self.window = event_loop
|
||||
.create_window(self.window_attributes.clone())
|
||||
.ok();
|
||||
self.window.create_window(event_loop)
|
||||
.map_err(|err| format!("Failed to create window: {}", err))
|
||||
.unwrap();
|
||||
|
||||
self.instance = self.window
|
||||
.as_ref()
|
||||
.and_then(|w| Some(VkInstance::new(w)));
|
||||
|
||||
if let Some(instance) = self.instance.as_ref() {
|
||||
let physical_devices = instance.get_physical_devices();
|
||||
|
||||
log::info!("Physical Devices:");
|
||||
for physical_device in physical_devices {
|
||||
log::info!("{}", physical_device)
|
||||
}
|
||||
}
|
||||
self.init_vulkan();
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) {
|
||||
|
@ -55,10 +73,7 @@ impl ApplicationHandler for App {
|
|||
log::info!("The close button was pressed; stopping");
|
||||
event_loop.exit();
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
self.window.as_ref().unwrap().request_redraw();
|
||||
}
|
||||
_ => (),
|
||||
_ => self.window.window_event(event_loop, id, event),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
mod app;
|
||||
pub use app::App;
|
||||
mod window;
|
||||
|
||||
pub use app::App;
|
||||
pub use window::Window;
|
63
src/display/window.rs
Normal file
63
src/display/window.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use std::ffi::c_char;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
use winit::raw_window_handle::HasDisplayHandle;
|
||||
use winit::window::WindowId;
|
||||
|
||||
pub struct Window {
|
||||
handle: Option<winit::window::Window>,
|
||||
window_attributes: winit::window::WindowAttributes
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(window_attributes: winit::window::WindowAttributes) -> Self {
|
||||
Self {
|
||||
handle: None,
|
||||
window_attributes,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_window(&mut self, event_loop: &ActiveEventLoop) -> anyhow::Result<()> {
|
||||
let window = event_loop.create_window(self.window_attributes.clone())?;
|
||||
|
||||
self.handle = Some(window);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn required_extensions(&self) -> anyhow::Result<Vec<*const c_char>> {
|
||||
let display_handle = self.handle
|
||||
.as_ref()
|
||||
.ok_or_else(||anyhow::anyhow!("Window not found"))?
|
||||
.display_handle()?;
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut extension_names = ash_window::enumerate_required_extensions(display_handle.as_raw())?
|
||||
.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());
|
||||
}
|
||||
|
||||
Ok(extension_names)
|
||||
}
|
||||
|
||||
pub fn handle(&self) -> Option<&winit::window::Window> {
|
||||
self.handle.as_ref()
|
||||
}
|
||||
|
||||
pub fn window_event(&mut self, _event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
|
||||
match event {
|
||||
WindowEvent::RedrawRequested => {
|
||||
match self.handle.as_ref() {
|
||||
Some(window) => window.request_redraw(),
|
||||
None => log::warn!("Redraw requested but no window found")
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
use winit::event_loop::EventLoop;
|
||||
use winit::window::Window;
|
||||
|
||||
mod display;
|
||||
mod vulkan;
|
||||
|
@ -9,7 +8,7 @@ fn main() {
|
|||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window_attributes = Window::default_attributes()
|
||||
let window_attributes = winit::window::Window::default_attributes()
|
||||
.with_title("Rust ASH Test")
|
||||
.with_visible(true)
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(
|
||||
|
@ -17,7 +16,8 @@ fn main() {
|
|||
f64::from(600),
|
||||
));
|
||||
|
||||
let mut app = display::App::new(window_attributes);
|
||||
let window = display::Window::new(window_attributes);
|
||||
let mut app = display::App::new(window);
|
||||
|
||||
let _ = event_loop.run_app(&mut app);
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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)
|
||||
)
|
||||
}
|
38
src/vulkan/utils/layers.rs
Normal file
38
src/vulkan/utils/layers.rs
Normal 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
11
src/vulkan/utils/mod.rs
Normal 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)
|
||||
)
|
||||
}
|
|
@ -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:")
|
||||
}
|
||||
}
|
3
src/vulkan/vk_logical_device.rs
Normal file
3
src/vulkan/vk_logical_device.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub struct VkLogicalDevice {
|
||||
|
||||
}
|
|
@ -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
29
src/vulkan/vk_surface.rs
Normal 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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue