Add logical device struct and surface handling for Vulkan
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…
Add table
Add a link
Reference in a new issue