new architecture

This commit is contained in:
zack 2025-03-26 20:02:58 -04:00
parent 9f7e72b784
commit 9cfd9d8b17
No known key found for this signature in database
GPG key ID: EE8A2B709E2401D1
28 changed files with 2625 additions and 5351 deletions

View file

@ -0,0 +1,223 @@
use ash::vk;
use parking_lot::Mutex;
use std::ffi::CStr;
use std::{collections::HashMap, sync::Arc};
use crate::error::{GfxHalError, Result};
use crate::instance::Instance;
use crate::physical_device::{PhysicalDevice, QueueFamilyIndices};
use crate::queue::Queue;
/// Represents the logical Vulkan device, created from a `PhysicalDevice`.
///
/// Owns the `ash::Device` and provides access to device functions and queues.
pub struct Device {
instance: Arc<Instance>,
physical_device: vk::PhysicalDevice,
device: ash::Device,
queues: Mutex<HashMap<(u32, u32), Arc<Queue>>>,
graphics_queue_family_index: u32,
compute_queue_family_index: Option<u32>,
transfer_queue_family_index: Option<u32>,
}
impl Device {
/// Creates a new logical device. Typically called via `PhysicalDevice::create_logical_device`.
///
/// # Saftey
/// - `instance` and `physical_device_handle` must be valid.
/// - `queue_family_indicies` must be valid indicies obtained from the `physical_device_handle`.
/// - `required_extensions` must be supported by the `physical_device_handle`.
/// - `enabled_features` and `mesh_features` must be supported by the `physical_device_handle`.
pub(crate) unsafe fn new(
instance: Arc<Instance>,
physical_device_handle: vk::PhysicalDevice,
queue_family_indicies: &QueueFamilyIndices,
required_extensions: &[&CStr],
enabled_features: &vk::PhysicalDeviceFeatures,
mesh_features: Option<&vk::PhysicalDeviceMeshShaderFeaturesEXT>,
) -> Result<Arc<Self>> {
let mut queue_create_infos = Vec::new();
let mut unique_queue_families = std::collections::HashSet::new();
let graphics_family = queue_family_indicies.graphics_family.ok_or_else(|| {
GfxHalError::MissingQueueFamily("Graphics Queue Family Missing".to_string())
})?;
unique_queue_families.insert(graphics_family);
if let Some(compute_family) = queue_family_indicies.compute_family {
unique_queue_families.insert(compute_family);
}
if let Some(transfer_family) = queue_family_indicies.transfer_family {
unique_queue_families.insert(transfer_family);
}
let queue_priorities = [1.0f32];
for &family_index in &unique_queue_families {
let queue_create_info = vk::DeviceQueueCreateInfo::default()
.queue_family_index(family_index)
.queue_priorities(&queue_priorities);
queue_create_infos.push(queue_create_info);
}
let extension_names_raw: Vec<*const i8> =
required_extensions.iter().map(|s| s.as_ptr()).collect();
let mut features2 = vk::PhysicalDeviceFeatures2::default().features(*enabled_features);
let mut mesh_features_copy;
if let Some(mesh_feats) = mesh_features {
mesh_features_copy = *mesh_feats;
features2 = features2.push_next(&mut mesh_features_copy);
}
let device_create_info = vk::DeviceCreateInfo::default()
.queue_create_infos(&queue_create_infos)
.enabled_extension_names(&extension_names_raw)
.push_next(&mut features2);
tracing::info!(
"Creating logical device with extensions: {:?}",
required_extensions
);
let device = instance.ash_instance().create_device(
physical_device_handle,
&device_create_info,
None,
)?;
tracing::info!("logical device created successfully.");
let mut queues_map = HashMap::new();
let arc_device_placeholder = Arc::new(Self {
instance,
physical_device: physical_device_handle,
device,
queues: Mutex::new(HashMap::new()),
graphics_queue_family_index: graphics_family,
compute_queue_family_index: queue_family_indicies.compute_family,
transfer_queue_family_index: queue_family_indicies.transfer_family,
});
for &family_index in &unique_queue_families {
let queue_handler = arc_device_placeholder
.device
.get_device_queue(family_index, 0);
let queue_wrapper = Arc::new(Queue::new(
Arc::clone(&arc_device_placeholder),
queue_handler,
family_index,
));
queues_map.insert((family_index, 0), queue_wrapper);
}
let device_handle = unsafe {
arc_device_placeholder
.instance
.ash_instance()
.create_device(physical_device_handle, &device_create_info, None)?
};
let final_device = Arc::new(Self {
instance: Arc::clone(&arc_device_placeholder.instance), // Clone from placeholder
physical_device: physical_device_handle,
device: device_handle, // Use the newly created handle
queues: Mutex::new(queues_map), // Use the populated map
graphics_queue_family_index: graphics_family,
compute_queue_family_index: queue_family_indicies.compute_family,
transfer_queue_family_index: queue_family_indicies.transfer_family,
});
Ok(final_device)
}
/// Provides raw access to the underlying `ash::Device`.
/// Use with caution, prefer safe wrappers where possible.
pub fn raw(&self) -> &ash::Device {
&self.device
}
/// Gets the handle of the physical device this logical device was created from.
pub fn physical_device_handle(&self) -> vk::PhysicalDevice {
self.physical_device
}
/// Gets the primary graphics queue family index used by this device.
pub fn graphics_queue_family_index(&self) -> u32 {
self.graphics_queue_family_index
}
/// Gets the compute queue family index, if a distinct one was found/used.
pub fn compute_queue_family_index(&self) -> Option<u32> {
self.compute_queue_family_index
}
/// Gets the transfer queue family index, if a distinct one was found/used.
pub fn transfer_queue_family_index(&self) -> Option<u32> {
self.transfer_queue_family_index
}
/// Gets a wrapped queue handle.
/// Currently only supports queue index 0 for each family.
pub fn get_queue(&self, family_index: u32, queue_index: u32) -> Option<Arc<Queue>> {
if queue_index != 0 {
tracing::warn!("get_queue currently only supports queue_index 0");
return None;
}
self.queues
.lock()
.get(&(family_index, queue_index))
.cloned()
}
/// Gets the primary graphics queue (family index from `graphics_queue_family_index`, queue index 0).
/// Panics if the graphics queue wasn't successfully created.
pub fn get_graphics_queue(&self) -> Arc<Queue> {
self.get_queue(self.graphics_queue_family_index, 0)
.expect("Graphics queue should always exist")
}
/// Waits until the logical device becomes idle.
/// This is a heavy operation and should be used sparingly (e.g., before destruction).
pub fn wait_idle(&self) -> Result<()> {
tracing::debug!("Waiting for device idle...");
unsafe { self.device.device_wait_idle()? };
tracing::debug!("Device idle.");
Ok(())
}
}
impl Drop for Device {
fn drop(&mut self) {
tracing::debug!("Destroying logical device...");
if let Err(e) = self.wait_idle() {
tracing::error!("Error waiting for device idle during drop: {}", e);
}
unsafe {
self.device.destroy_device(None);
}
tracing::debug!("Logical device destroyed.");
}
}
impl PhysicalDevice {
/// Creates the logical device (`Device`) from this physical device.
///
/// # Safety
/// See `Device::new` safety comments.
pub unsafe fn create_logical_device(
&self,
required_extensions: &[&CStr],
queue_family_indices: &QueueFamilyIndices,
enabled_features: &vk::PhysicalDeviceFeatures,
mesh_features: Option<&vk::PhysicalDeviceMeshShaderFeaturesEXT>,
) -> Result<Arc<Device>> {
Device::new(
Arc::clone(self.instance()),
self.handle(),
queue_family_indices,
required_extensions,
enabled_features,
mesh_features,
)
}
}

View file

@ -0,0 +1,64 @@
use ash::vk;
use thiserror::Error;
/// Top-level error type for the gfx_hal crate.
#[derive(Error, Debug)]
pub enum GfxHalError {
/// Error originating directly from a Vulkan API call.
#[error("Vulkan API Error: {0}")]
VulkanError(#[from] vk::Result),
/// Error loading the Vulkan library or resolving function pointers.
#[error("Failed to load Vulkan library: {0}")]
LoadError(String),
/// No suitable physical device (GPU) could be found that meets requirements.
#[error("No suitable physical device found: {0}")]
NoSuitableGpu(String),
/// A required Vulkan instance or device extension is not supported.
#[error("Missing required Vulkan extension: {0:?}")]
MissingExtension(String),
/// A required Vulkan feature is not supported by the physical device.
#[error("Missing required Vulkan feature.")]
MissingFeature,
/// Failed to find a suitable queue family (e.g., graphics, present).
#[error("Could not find required queue family: {0}")]
MissingQueueFamily(String),
/// Error related to window system integration surface creation.
#[error("Failed to create Vulkan surface: {0}")]
SurfaceCreationError(vk::Result),
/// The Vulkan surface became invalid (e.g., window resized, closed).
#[error("Vulkan surface is no longer valid (maybe lost or out of date)")]
SurfaceLost,
/// Error converting a C-style string.
#[error("Invalid C string: {0}")]
InvalidCString(#[from] std::ffi::NulError),
/// Error converting C string slice to Rust string slice.
#[error("Invalid UTF-8 sequence in C string: {0}")]
InvalidCStringUtf8(#[from] std::ffi::FromBytesWithNulError),
/// Generic I/O error.
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
/// Error with winit windowing library.
#[error("Winit Error: {0}")]
WinitHandleError(#[from] winit::raw_window_handle::HandleError),
/// Ash loader error.
#[error("Error loading the ash entry.")]
AshEntryError(#[from] ash::LoadingError),
/// Placeholder for other specific errors.
#[error("An unexpected error occurred: {0}")]
Other(String),
}
pub type Result<T, E = GfxHalError> = std::result::Result<T, E>;

View file

@ -0,0 +1,353 @@
use std::{
collections::HashSet,
ffi::{c_char, c_void, CStr, CString},
sync::Arc,
};
use ash::{ext::debug_utils, vk};
use winit::raw_window_handle::{DisplayHandle, HasDisplayHandle};
use crate::error::{GfxHalError, Result};
unsafe extern "system" fn vulkan_debug_callback(
message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
message_type: vk::DebugUtilsMessageTypeFlagsEXT,
p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT,
_p_user_data: *mut c_void,
) -> vk::Bool32 {
let callback_data = *p_callback_data;
let message_id_number: i32 = callback_data.message_id_number;
let message_id_name = if callback_data.p_message_id_name.is_null() {
std::borrow::Cow::from("")
} else {
CStr::from_ptr(callback_data.p_message_id_name).to_string_lossy()
};
let message = if callback_data.p_message.is_null() {
std::borrow::Cow::from("")
} else {
CStr::from_ptr(callback_data.p_message).to_string_lossy()
};
let severity = match message_severity {
vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => "[VERBOSE]",
vk::DebugUtilsMessageSeverityFlagsEXT::INFO => "[INFO]",
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => "[WARNING]",
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => "[ERROR]",
_ => "[UNKNOWN SEVERITY]",
};
let ty = match message_type {
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL => "[GENERAL]",
vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION => "[VALIDATION]",
vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE => "[PERFORMANCE]",
_ => "[UNKNOWN TYPE]",
};
// Use the tracing crate for output
match message_severity {
vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => {
tracing::debug!(
"{} {} ({}:{}) {}",
severity,
ty,
message_id_name,
message_id_number,
message
);
}
vk::DebugUtilsMessageSeverityFlagsEXT::INFO => {
tracing::info!(
"{} {} ({}:{}) {}",
severity,
ty,
message_id_name,
message_id_number,
message
);
}
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => {
tracing::warn!(
"{} {} ({}:{}) {}",
severity,
ty,
message_id_name,
message_id_number,
message
);
}
// Treat ERROR and higher as errors
_ => {
tracing::error!(
"{} {} ({}:{}) {}",
severity,
ty,
message_id_name,
message_id_number,
message
);
}
}
vk::FALSE // Standard return value
}
#[derive(Clone, Debug)]
pub struct InstanceConfig {
pub application_name: String,
pub engine_name: String,
pub application_version: u32,
pub engine_version: u32,
/// Enable Vulkan validation layers
pub enable_validation: bool,
/// Additional required instance extensions beyond surface/debug.
pub required_extensions: Vec<&'static CStr>,
}
impl Default for InstanceConfig {
fn default() -> Self {
InstanceConfig {
application_name: "Defualt App".to_string(),
engine_name: "Default Engine".to_string(),
application_version: vk::make_api_version(0, 1, 0, 0),
engine_version: vk::make_api_version(0, 1, 0, 0),
enable_validation: cfg!(debug_assertions),
required_extensions: Vec::new(),
}
}
}
/// Represents the Vulkan API Instance
///
/// Owns the `ash::Entry`, `ash::Instance` and potentially the debug messenger.
/// This is the starting point for interacting with Vulkan
pub struct Instance {
entry: ash::Entry,
instance: ash::Instance,
debug_utils: Option<ash::ext::debug_utils::Instance>,
debug_messenger: Option<vk::DebugUtilsMessengerEXT>,
}
impl Instance {
/// Creates a new Vulkan `Instance`
///
/// # Arguments
/// * `config` - Configuration settings for the instance
/// * `display_handle` - Raw display handle for the windowing system
/// * `external_extensions` - A slice of `CString` representing additional required instance extensions,
/// typically provided by integration libraries like `egui-ash`
pub fn new(
config: &InstanceConfig,
display_handle: &dyn HasDisplayHandle,
external_extentions: &[CString],
) -> Result<Arc<Self>> {
let entry = unsafe { ash::Entry::load()? };
let app_name = CString::new(config.application_name.clone())?;
let engine_name = CString::new(config.engine_name.clone())?;
let app_info = vk::ApplicationInfo::default()
.application_name(&app_name)
.application_version(config.application_version)
.engine_name(&engine_name)
.engine_version(config.engine_version)
.api_version(vk::API_VERSION_1_3);
let validation_layers = [c"VK_LAYER_KHRONOS_validation"];
let enabled_layer_names_raw: Vec<*const c_char> = if config.enable_validation
&& Self::check_validation_layer_support(&entry, &validation_layers)?
{
tracing::info!("Validation layers enabled.");
validation_layers.iter().map(|name| name.as_ptr()).collect()
} else {
if config.enable_validation {
tracing::warn!("Validation layers requested but not supported. Disabling.");
}
Vec::new()
};
let display_handle: DisplayHandle = display_handle.display_handle()?;
let surface_extensions_ptrs =
ash_window::enumerate_required_extensions(display_handle.into())?;
let mut required_cstrs_for_check: Vec<&CStr> = Vec::new();
let surface_extensions_cstrs: Vec<&CStr> = surface_extensions_ptrs
.iter()
.map(|&ptr| unsafe { CStr::from_ptr(ptr) })
.collect();
required_cstrs_for_check.extend(&surface_extensions_cstrs);
required_cstrs_for_check.extend(external_extentions.iter().map(|cs| cs.as_c_str()));
if config.enable_validation {
required_cstrs_for_check.push(ash::ext::debug_utils::NAME);
}
required_cstrs_for_check.sort_unstable();
required_cstrs_for_check.dedup();
Self::check_instance_extension_support(&entry, &required_cstrs_for_check)?;
tracing::info!(
"Required instance extensions supported: {:?}",
required_cstrs_for_check
);
let mut enabled_extension_names_raw: Vec<*const c_char> = Vec::new();
enabled_extension_names_raw.extend(surface_extensions_ptrs);
enabled_extension_names_raw.extend(external_extentions.iter().map(|cs| cs.as_ptr()));
if config.enable_validation {
enabled_extension_names_raw.push(ash::ext::debug_utils::NAME.as_ptr());
}
enabled_extension_names_raw.sort_unstable();
enabled_extension_names_raw.dedup();
let mut debug_create_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
.message_severity(
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
| vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
)
.message_type(
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
)
.pfn_user_callback(Some(vulkan_debug_callback));
let mut instance_create_info = vk::InstanceCreateInfo::default()
.application_info(&app_info)
.enabled_layer_names(&enabled_layer_names_raw)
.enabled_extension_names(&enabled_extension_names_raw);
if config.enable_validation {
instance_create_info = instance_create_info.push_next(&mut debug_create_info);
}
let instance = unsafe { entry.create_instance(&instance_create_info, None)? };
tracing::info!("Vulkan instance created succesfully.");
let (debug_utils, debug_messenger) = if config.enable_validation {
let utils = ash::ext::debug_utils::Instance::new(&entry, &instance);
let messenger =
unsafe { utils.create_debug_utils_messenger(&debug_create_info, None)? };
tracing::debug!("Debug messenger created.");
(Some(utils), Some(messenger))
} else {
(None, None)
};
Ok(Arc::new(Self {
entry,
instance,
debug_utils,
debug_messenger,
}))
}
/// Provides access to the loaded Vulkan entry points.
pub fn entry(&self) -> &ash::Entry {
&self.entry
}
/// Provides access to the raw `ash::Instance`.
pub fn ash_instance(&self) -> &ash::Instance {
&self.instance
}
/// Provides access to the DebugUtils extension loader, if enabled.
pub fn debug_utils(&self) -> Option<&debug_utils::Instance> {
self.debug_utils.as_ref()
}
/// Checks if the requested validation layers are available.
fn check_validation_layer_support(
entry: &ash::Entry,
required_layers: &[&CStr],
) -> Result<bool> {
let available_layers = unsafe { entry.enumerate_instance_layer_properties()? };
let available_names: HashSet<&CStr> = available_layers
.iter()
.map(|layer| unsafe { CStr::from_ptr(layer.layer_name.as_ptr()) })
.collect();
for layer in required_layers {
if !available_names.contains(layer) {
tracing::warn!("Required validation layer {:?} not found.", layer);
return Ok(false);
}
}
Ok(true)
}
/// Checks if the requested instance extensions are available.
/// Takes a deduplicated list of required extension names (&CStr).
fn check_instance_extension_support(
entry: &ash::Entry,
required_extensions: &[&CStr],
) -> Result<()> {
let available_extensions = unsafe { entry.enumerate_instance_extension_properties(None)? };
let available_names: HashSet<&CStr> = available_extensions
.iter()
.map(|ext| unsafe { CStr::from_ptr(ext.extension_name.as_ptr()) })
.collect();
tracing::debug!("Available instance extensions: {:?}", available_names);
for ext in required_extensions {
if !available_names.contains(ext) {
tracing::error!("Missing required instance extension: {:?}", ext);
return Err(GfxHalError::MissingExtension(
ext.to_string_lossy().into_owned(),
));
}
}
Ok(())
}
// /// Enumerates all physical devices available to this instance.
// ///
// /// # Safety
// /// The `Instance` must be kept alive while the returned `PhysicalDevices`s are in use.
// /// This is ensured by returning `PhysicalDevice`s holding an `Arc<Instance>`.
// pub unsafe fn enumerate_phyiscal_devices(self: &Arc<Self>) -> Result<Vec<PhysicalDevice>> {
// let physical_device_handles = self.instance.enumerate_physical_devices()?;
//
// if physical_device_handles.is_empty() {
// return Err(GfxHalError::NoSuitableGpu(
// "No Vulkan-compatibile GPUs found.".to_string(),
// ));
// }
//
// let devices = physical_device_handles
// .into_iter()
// .map(|handle| PhysicalDevice::new(Arc::clone(self), handle))
// .collect()?;
//
// Ok(devices)
// }
// /// Creates a vulkan surface for the given window
// ///
// /// # Safety
// /// The `window_handle_trait_obj` must point to a valid window/display managed by caller.
// /// The `Instance` must be kept alive longer than the returned `Surface`
// pub unsafe fn create_surface(
// self: &Arc<Self>,
// window_handle_trait_obj: &(impl HasWindowHandle + HasDisplayHandle),
// ) -> Result<Arc<Surface>> {
// Surface::new(Arc::clone(self), window_handle_trait_obj)
// }
}
impl Drop for Instance {
fn drop(&mut self) {
unsafe {
if let (Some(utils), Some(messenger)) = (&self.debug_utils, self.debug_messenger) {
tracing::debug!("Destroying debug messenger...");
utils.destroy_debug_utils_messenger(messenger, None);
}
tracing::debug!("Destroying Vulkan instance...");
self.instance.destroy_instance(None);
tracing::debug!("Vulkan instance destroyed");
}
}
}

View file

@ -0,0 +1,8 @@
pub mod device;
pub mod error;
pub mod instance;
pub mod physical_device;
pub mod queue;
pub mod surface;
pub mod swapchain;
pub mod sync;

View file

@ -0,0 +1,164 @@
use ash::vk;
use crate::{error::GfxHalError, instance::Instance};
use std::{ffi::CStr, sync::Arc};
/// Represents a physical Vulkan device (GPU).
///
/// This struct holds a handle to the Vulkan physical device and a
/// reference back to the `Instance` it originated from. It does *not* own
/// the `vk::PhysicalDevice` in the sense that it doesn't destroy it; physical
/// devices are implicitly managed by the `vk::Instance`
///
/// It's cheap to clone as it only clones the `Arc<Instance>` and copies the handle
#[derive(Clone)]
pub struct PhysicalDevice {
/// Shared reference to the Vulkan instance
instance: Arc<Instance>,
/// The raw Vulkan physical device handle.
handle: vk::PhysicalDevice,
}
/// Holds information about queue families found on a `PhysicalDevice`.
#[derive(Debug, Clone)]
pub struct QueueFamilyIndices {
/// Queue family index supporting graphics operations.
pub graphics_family: Option<u32>,
/// Queue family index supporting compute operations.
pub compute_family: Option<u32>,
/// Queue family index supporting transfer operations.
pub transfer_family: Option<u32>,
/// Queue family index supporting presentaiton to a given surface.
/// This might be the same as the graphics family.
pub present_family: Option<u32>,
}
impl QueueFamilyIndices {
/// Checks if all essential queue families (graphics, present if surface exists) were found.
pub fn is_complete(&self, requires_present: bool) -> bool {
self.graphics_family.is_some() && (!requires_present || self.present_family.is_some())
}
}
/// Represents the suitability of a physical device.
#[derive(Debug)]
pub enum Suitability<'a> {
/// The device is suitable and meets requirements.
Suitable {
/// A score indicating preference (higher is better).
score: u32,
/// The indicies of the required queue families.
indicies: QueueFamilyIndices,
/// The properties of the device.
properties: Box<vk::PhysicalDeviceProperties>,
/// The supported base features of the device.
features: Box<vk::PhysicalDeviceFeatures>,
/// THe supported mesh shader features.
mesh_shader_features: vk::PhysicalDeviceMeshShaderFeaturesEXT<'a>,
},
/// The device is not suitable.
NotSuitable {
/// The reason why the device is not suitable.
reason: String,
},
}
impl PhysicalDevice {
/// Creates a new `PhysicalDevice` wrapper
/// Typically called internally by `Instance::enumerate_physical_devices`
pub(crate) fn new(instance: Arc<Instance>, handle: vk::PhysicalDevice) -> Self {
Self { instance, handle }
}
/// Gets the raw `vk::PhysicalDevice` handle.
pub fn handle(&self) -> vk::PhysicalDevice {
self.handle
}
/// Gets a reference to the `Instance` this device belongs to.
pub fn instance(&self) -> &Arc<Instance> {
&self.instance
}
/// Queries the basic properties of the physical device.
///
/// # Safety
/// Assumes the `PhysicalDevice` handle is valid.
pub unsafe fn get_properties(&self) -> vk::PhysicalDeviceProperties {
self.instance
.ash_instance()
.get_physical_device_properties(self.handle)
}
/// Queries the supported features, including mesh shaders.
///
/// # Safety
/// Assumes the `PhysicalDevice` handle is valid.
pub unsafe fn get_features(
&self,
) -> (
vk::PhysicalDeviceFeatures,
vk::PhysicalDeviceMeshShaderFeaturesEXT,
) {
let mut mesh_shader_features = vk::PhysicalDeviceMeshShaderFeaturesEXT::default();
let mut features2 =
vk::PhysicalDeviceFeatures2::default().push_next(&mut mesh_shader_features);
self.instance
.ash_instance()
.get_physical_device_features2(self.handle, &mut features2);
(features2.features, mesh_shader_features)
}
/// Queries the properties of all queue families available on the device.
///
/// # Safety
/// Assumes the `PhysicalDevice` handle is valid.
pub unsafe fn get_queue_family_properties(&self) -> Vec<vk::QueueFamilyProperties> {
self.instance
.ash_instance()
.get_physical_device_queue_family_properties(self.handle)
}
/// Queries the device specific extensions supported by this physical device.
///
/// # Safety
/// Assumes the `PhysicalDevice` handle is valid.
pub unsafe fn get_supported_extensions(
&self,
) -> Result<Vec<vk::ExtensionProperties>, GfxHalError> {
self.instance
.ash_instance()
.enumerate_device_extension_properties(self.handle)
.map_err(GfxHalError::VulkanError)
}
// /// Finds suitable queue family indicies based on required flags and optional surface.
// ///
// /// # Safety
// /// Assumes the `PhysicalDevice` handle and `Surface` (if provided) are valid.
// pub unsafe fn find_queue_families(
// &self,
// surface: Option<&Surface>,
// ) -> Result<QueueFamilyIndices, GfxHalError> {
// }
// /// Checks if the physical device meets the specified requirements and scores it.
// ///
// /// # Arguments
// /// * `required_extensions` - A slice of C-style strings representing required device extensions (e.g., `ash::extensions::khr::Swapchain::name()`).
// /// * `required_mesh_features` - The minimum mesh shader features required. Check `task_shader` and `mesh_shader` fields.
// /// * `surface` - An optional surface to check for presentation support.
// ///
// /// # Safety
// /// Assumes the `PhysicalDevice` handle and `Surface` (if provided) are valid.
// pub unsafe fn check_suitability(
// &self,
// required_extensions: &[&CStr],
// required_mesh_features: &vk::PhysicalDeviceMeshShaderFeaturesEXT,
// surface: Option<&Surface>,
// ) -> Result<Suitability, GfxHalError> {
// }
}

102
crates/gfx_hal/src/queue.rs Normal file
View file

@ -0,0 +1,102 @@
use std::sync::Arc;
use ash::vk;
use parking_lot::Mutex;
use crate::device::Device;
use crate::error::Result;
use crate::sync::Fence;
/// Represents a Vulkan device queue.
///
/// Holds a reference to the `Device` and the raw `vk::Queue` handle.
/// Provides methods for submitting command buffers.
pub struct Queue {
device: Arc<Device>,
queue: vk::Queue,
family_index: u32,
// Each queue submission must be externally synchronized or locked internally.
// Using a Mutex here provides a simple internal locking per queue.
submit_lock: Mutex<()>,
}
impl Queue {
/// Creates a new Queue wrapper. Called internally by `Device`.
pub(crate) fn new(device: Arc<Device>, queue: vk::Queue, family_index: u32) -> Self {
Self {
device,
queue,
family_index,
submit_lock: Mutex::new(()),
}
}
/// Gets the raw `vk::Queue` handle.
pub fn handle(&self) -> vk::Queue {
self.queue
}
/// Gets the queue family index this queue belongs to.
pub fn family_index(&self) -> u32 {
self.family_index
}
/// Gets a reference to the logical device this queue belongs to.
pub fn device(&self) -> &Arc<Device> {
&self.device
}
/// Submits command buffers to the queue.
///
/// This method acquires an internal lock for the duration of the submission call
/// to prevent concurrent `vkQueueSubmit` calls on the same queue from this wrapper.
///
/// # Arguments
/// * `submits` - A slice of `vk::SubmitInfo` describing the work to submit.
/// * `signal_fence` - An optional `Fence` to signal when the submission completes.
///
/// # Safety
/// - The command buffers and synchronization primitieves within `submits` must be valid.
/// - The `signal_fence`, if provided, must be valid and unsignaled.
pub unsafe fn submit(
&self,
submits: &[vk::SubmitInfo],
signal_fence: Option<&Fence>,
) -> Result<()> {
let fence_handle = signal_fence.map_or(vk::Fence::null(), |f| f.handle());
let _lock = self.submit_lock.lock();
tracing::trace!(
"Submitting {} batch(es) to queue family {}",
submits.len(),
self.family_index
);
self.device
.raw()
.queue_submit(self.queue, submits, fence_handle)?;
tracing::trace!("Submission successful.");
Ok(())
}
/// Waits until this queue becomes idle.
///
/// This is a heavy operation and blocks the current thread.
pub fn wait_idle(&self) -> Result<()> {
tracing::debug!("Waiting for queue idle (family {})...", self.family_index);
// Lock the mutex while waiting to prevent submissions during the wait?
// Or allow submissions and let Vulkan handle it? Let Vulkan handle it.
unsafe { self.device.raw().queue_wait_idle(self.queue)? };
tracing::debug!("Queue idle (family {}).", self.family_index);
Ok(())
}
// Note: vkQueuePresentKHR is intentionally omitted here.
// Presentation is tightly coupled with Swapchain. It's safer to
// have a method like `Swapchain::present(&self, queue: &Queue, ...)`
// which internally calls `queue.device().raw().queue_present_khr(...)`
// using the swapchain's loader.
// If direct access is needed, it can be done with `queue.device().raw()`.
}
// Queues don't own the vk::Queue handle (the Device does), so no Drop impl.

View file

@ -0,0 +1,126 @@
use crate::{
error::{GfxHalError, Result},
instance::Instance,
};
use ash::{khr::surface::Instance as SurfaceLoader, vk};
use std::sync::Arc;
use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle};
/// Represents a Vulkan presentation surface, tied to a window.
///
/// Owns the `vk::SurfaceKHR` handle and the `ash` Surface loader extension.
pub struct Surface {
instance: Arc<Instance>,
surface_loader: SurfaceLoader,
surface: vk::SurfaceKHR,
}
impl Surface {
/// Creates a new Vulkan `Surface`
///
/// # Safety
/// - The `window_handle_trait_obj` must provide valid window and display handles
/// for the lifetime of the `Surface`.
/// - The `Instance` must outlive the `Surface`
pub unsafe fn new(
instance: Arc<Instance>,
window_handle_trait_obj: &(impl HasWindowHandle + HasDisplayHandle),
) -> Result<Arc<Self>> {
let surface_loader = SurfaceLoader::new(instance.entry(), instance.ash_instance());
let surface = ash_window::create_surface(
instance.entry(),
instance.ash_instance(),
window_handle_trait_obj.display_handle()?.into(),
window_handle_trait_obj.window_handle()?.into(),
None,
)
.map_err(GfxHalError::SurfaceCreationError)?;
tracing::info!("Vulkan surface created successfully.");
Ok(Arc::new(Self {
instance,
surface_loader,
surface,
}))
}
/// Gets the raw `vk::SurfaceKHR` handle.
pub fn handle(&self) -> vk::SurfaceKHR {
self.surface
}
/// Gets a reference to the `ash` Surface loader extension.
pub fn surface_loader(&self) -> &SurfaceLoader {
&self.surface_loader
}
/// Gets a reference to the `Instance` this surface belongs to
pub fn instance(&self) -> &Arc<Instance> {
&self.instance
}
/// Queries surface capabilites for a given physical device.
///
/// # Safety
/// The `physical_device` handle must be valid and compatible with this surface.
pub unsafe fn get_physical_device_surface_capabilities(
&self,
physical_device: vk::PhysicalDevice,
) -> Result<vk::SurfaceCapabilitiesKHR> {
self.surface_loader
.get_physical_device_surface_capabilities(physical_device, self.surface)
.map_err(GfxHalError::VulkanError)
}
/// Queries supported surface formats for a given physical device.
///
/// # Safety
/// The `physical_device` handle must be valid and compatible with this surface.
pub unsafe fn get_physical_device_surface_formats(
&self,
physical_device: vk::PhysicalDevice,
) -> Result<Vec<vk::SurfaceFormatKHR>> {
self.surface_loader
.get_physical_device_surface_formats(physical_device, self.surface)
.map_err(GfxHalError::VulkanError)
}
/// Queries supported present modes for a given physical device.
///
/// # Safety
/// The `physical_device` handle must be valid and compatible with this surface.
pub unsafe fn get_physical_device_surface_present_modes(
&self,
physical_device: vk::PhysicalDevice,
) -> Result<Vec<vk::PresentModeKHR>> {
self.surface_loader
.get_physical_device_surface_present_modes(physical_device, self.surface)
.map_err(GfxHalError::VulkanError)
}
/// Queries surface support for a given queue family index on a physical device.
///
/// # Safety
/// The `physical_device` handle must be valid and compatible with this surface.
pub unsafe fn get_physical_device_surface_support(
&self,
physical_device: vk::PhysicalDevice,
queue_family_index: u32,
) -> Result<bool> {
self.surface_loader
.get_physical_device_surface_support(physical_device, queue_family_index, self.surface)
.map_err(GfxHalError::VulkanError)
}
}
impl Drop for Surface {
fn drop(&mut self) {
tracing::debug!("Destroying Vulkan surface...");
unsafe {
self.surface_loader.destroy_surface(self.surface, None);
}
tracing::debug!("Vulkan surface destroyed.");
}
}

View file

@ -0,0 +1,321 @@
use std::sync::Arc;
use ash::khr::swapchain::Device as SwapchainLoader;
use ash::vk;
use crate::device::{self, Device};
use crate::error::{GfxHalError, Result};
use crate::surface::{self, Surface};
use crate::sync::{Fence, Semaphore};
/// Configuration for creating or recreating a `Swapchain`.
#[derive(Clone, Debug)]
pub struct SwapchainConfig {
/// Desired number of images in the swapchain (min/max clamped by capabilities).
pub desired_image_count: u32,
/// Preferred surface format (e.g., `vk::Format::B8G8R8A8_SRGB`).
pub desired_format: vk::SurfaceFormatKHR,
/// Preferred presentation mode (e.g., `vk::PresentModeKHR::MAILBOX`).
pub desired_present_mode: vk::PresentModeKHR,
/// Desired usage flags for swapchain images (e.g., `vk::ImageUsageFlags::COLOR_ATTACHMENT`).
pub image_usage: vk::ImageUsageFlags,
/// The dimensions of the surface.
pub extent: vk::Extent2D,
/// Transformation to apply (usually `vk::SurfaceTransformFlagsKHR::IDENTITY`).
pub pre_transform: vk::SurfaceTransformFlagsKHR,
/// Alpha compositing mode (usually `vk::CompositeAlphaFlagsKHR::OPAQUE`).
pub composite_alpha: vk::CompositeAlphaFlagsKHR,
}
/// Represents the Vulkan swapchain, managing presentation images.
///
/// Owns the `vk::SwapchainKHR`, the `ash` Swapchain loader, the swapchain images,
/// and their corresponding image views.
pub struct Swapchain {
device: Arc<Device>,
swapchain_loader: SwapchainLoader,
swapchain: vk::SwapchainKHR,
images: Vec<vk::Image>,
image_views: Vec<vk::ImageView>,
format: vk::SurfaceFormatKHR,
extent: vk::Extent2D,
image_count: u32,
}
impl Swapchain {
/// Creates a new `Swapchain` or recreates an exisiting one.
///
/// # Arguments
/// * `device` - The logical device.
/// * `surface` - The surface to present to.
/// * `config` - Desired swapchain configuration.
/// * `old_swapchain` - Optional handle to a previous swapchain for smoother recreation.
///
/// # Safety
/// - `device` and `surface` must be valid and compatible.
/// - If `old_swapchain` is provided, it must be a valid handle previously created
/// with the same `surface`.
/// - The caller must ensure that the `old_swapchain` (and its associated resources like
/// image views) are no longer in use before calling this function and are properly
/// destroyed *after* the new swapchain is successfully created.
pub unsafe fn new(
device: Arc<Device>,
surface: Arc<Surface>,
config: SwapchainConfig,
old_swapchain: Option<vk::SwapchainKHR>,
) -> Result<Self> {
let physical_device = device.physical_device_handle();
let capabilities = surface.get_physical_device_surface_capabilities(physical_device)?;
let formats = surface.get_physical_device_surface_formats(physical_device)?;
let present_modes = surface.get_physical_device_surface_present_modes(physical_device)?;
if formats.is_empty() || present_modes.is_empty() {
return Err(GfxHalError::NoSuitableGpu(
"Swapchain creation failed: No formats or present modes available.".to_string(),
));
}
let surface_format = Self::choose_surface_format(&formats, config.desired_format);
let present_mode = Self::choose_present_mode(&present_modes, config.desired_present_mode);
let extent = Self::choose_extent(capabilities, config.extent);
let image_count = Self::choose_image_count(capabilities, config.desired_image_count);
tracing::info!("Creating swapchain: Format={:?}, ColorSpace={:?}, PresentMode={:?}, Extent={:?}, ImageCount={}", surface_format.format, surface_format.color_space, present_mode, extent, image_count);
let mut create_info = vk::SwapchainCreateInfoKHR::default()
.surface(surface.handle())
.min_image_count(image_count)
.image_format(surface_format.format)
.image_color_space(surface_format.color_space)
.image_extent(extent)
.image_array_layers(1)
.image_usage(config.image_usage)
.pre_transform(config.pre_transform)
.composite_alpha(config.composite_alpha)
.present_mode(present_mode)
.clipped(true);
let queue_family_indicies = [device.graphics_queue_family_index()];
create_info = create_info
.image_sharing_mode(vk::SharingMode::EXCLUSIVE)
.queue_family_indices(&queue_family_indicies);
if let Some(old) = old_swapchain {
create_info = create_info.old_swapchain(old);
tracing::debug!("Passing old swapchain handle for recreation.");
}
let swapchain_loader =
SwapchainLoader::new(surface.instance().ash_instance(), device.raw());
let swapchain = swapchain_loader.create_swapchain(&create_info, None)?;
tracing::info!("Swapchain created successfully.");
let images = swapchain_loader.get_swapchain_images(swapchain)?;
tracing::debug!("Retrieved {} swapchain images.", images.len());
let image_views = Self::create_image_views(device.raw(), &images, surface_format.format)?;
tracing::debug!("Created {} swapchain image views.", image_views.len());
Ok(Self {
device,
swapchain_loader,
swapchain,
images,
image_views,
format: surface_format,
extent,
image_count,
})
}
/// Acquires the next available image from the swapchain.
///
/// Returns the index of the acquired image and a boolean indicating if the
/// swapchain is suboptimal (needs recreation).
///
/// # Safety
/// - `signal_semaphore` and `signal_fence`, if provided, must be valid handles
/// that are not currently waited on by the GPU.
/// - The caller must ensure proper synchronization before using the returned image index.
pub unsafe fn acquire_next_image(
&self,
timeout_ns: u64,
signal_semaphore: Option<&Semaphore>,
signal_fence: Option<&Fence>,
) -> Result<(u32, bool)> {
let semaphore_handle = signal_semaphore.map_or(vk::Semaphore::null(), |s| s.handle());
let fence_handle = signal_fence.map_or(vk::Fence::null(), |f| f.handle());
match self.swapchain_loader.acquire_next_image(
self.swapchain,
timeout_ns,
semaphore_handle,
fence_handle,
) {
Ok((image_index, suboptimal)) => Ok((image_index, suboptimal)),
Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => Err(GfxHalError::SurfaceLost),
Err(e) => Err(GfxHalError::VulkanError(e)),
}
}
/// Gets the raw `vk::SwapchainKHR` handle.
pub fn handle(&self) -> vk::SwapchainKHR {
self.swapchain
}
/// Gets a reference to the `ash` Swapchain loader extension
pub fn loader(&self) -> &SwapchainLoader {
&self.swapchain_loader
}
/// Gets the chosen surface format of the swapchain.
pub fn format(&self) -> vk::SurfaceFormatKHR {
self.format
}
/// Gets the extent (dimensions) of the swapchain image
pub fn extent(&self) -> vk::Extent2D {
self.extent
}
/// Gets the actual number of images in the swapchain
pub fn image_count(&self) -> u32 {
self.image_count
}
/// Gets a slice containing the raw `vk::Image` handles.
pub fn images(&self) -> &[vk::Image] {
&self.images
}
/// Gets a slice containing the raw `vk::ImageView` handles.
pub fn image_views(&self) -> &[vk::ImageView] {
&self.image_views
}
fn choose_surface_format(
available_formats: &[vk::SurfaceFormatKHR],
desired_format: vk::SurfaceFormatKHR,
) -> vk::SurfaceFormatKHR {
for format in available_formats {
if format.format == desired_format.format
&& format.color_space == desired_format.color_space
{
return *format;
}
}
tracing::warn!(
"Desired swapchain format {:?}/{:?} not available. Falling back to {:?}/{:?}.",
desired_format.format,
desired_format.color_space,
available_formats[0].format,
available_formats[0].color_space
);
available_formats[0]
}
fn choose_present_mode(
available_modes: &[vk::PresentModeKHR],
desired_mode: vk::PresentModeKHR,
) -> vk::PresentModeKHR {
if desired_mode == vk::PresentModeKHR::MAILBOX
&& available_modes.contains(&vk::PresentModeKHR::MAILBOX)
{
return vk::PresentModeKHR::MAILBOX;
}
if desired_mode == vk::PresentModeKHR::IMMEDIATE
&& available_modes.contains(&vk::PresentModeKHR::IMMEDIATE)
{
return vk::PresentModeKHR::IMMEDIATE;
}
vk::PresentModeKHR::FIFO
}
fn choose_extent(
capabilities: vk::SurfaceCapabilitiesKHR,
desired_extent: vk::Extent2D,
) -> vk::Extent2D {
if capabilities.current_extent.width != u32::MAX {
capabilities.current_extent
} else {
vk::Extent2D {
width: desired_extent.width.clamp(
capabilities.min_image_extent.width,
capabilities.max_image_extent.width,
),
height: desired_extent.height.clamp(
capabilities.min_image_extent.height,
capabilities.max_image_extent.height,
),
}
}
}
fn choose_image_count(capabilities: vk::SurfaceCapabilitiesKHR, desired_count: u32) -> u32 {
let mut count = desired_count.max(capabilities.min_image_count);
if capabilities.max_image_count > 0 {
count = count.min(capabilities.max_image_count);
}
count
}
unsafe fn create_image_views(
device: &ash::Device,
images: &[vk::Image],
format: vk::Format,
) -> Result<Vec<vk::ImageView>> {
images
.iter()
.map(|image| {
let create_info = vk::ImageViewCreateInfo::default()
.image(*image)
.view_type(vk::ImageViewType::TYPE_2D)
.format(format)
.components(vk::ComponentMapping {
r: vk::ComponentSwizzle::IDENTITY,
g: vk::ComponentSwizzle::IDENTITY,
b: vk::ComponentSwizzle::IDENTITY,
a: vk::ComponentSwizzle::IDENTITY,
})
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
});
device.create_image_view(&create_info, None)
})
.collect::<std::result::Result<Vec<_>, _>>()
.map_err(GfxHalError::VulkanError)
}
/// Destroys the image views associated with this swapchain.
/// Called internally by Drop and potentially during recreation.
unsafe fn destory_image_views(&mut self) {
tracing::debug!(
"Destroying {} swapchain image views...",
self.image_views.len()
);
for view in self.image_views.drain(..) {
self.device.raw().destroy_image_view(view, None);
}
tracing::debug!("Swapchain image views destroyed.")
}
}
impl Drop for Swapchain {
fn drop(&mut self) {
tracing::debug!("Destroying swapchain...");
unsafe {
self.destory_image_views();
self.swapchain_loader
.destroy_swapchain(self.swapchain, None);
}
tracing::debug!("Swapchain destroyed.")
}
}

183
crates/gfx_hal/src/sync.rs Normal file
View file

@ -0,0 +1,183 @@
use std::{sync::Arc, time::Duration};
use ash::vk;
use crate::{
device::Device,
error::{GfxHalError, Result},
};
/// Wraps a `vk::Fence`, used for CPU-GPU synchronization.
///
/// Owns the `vk::Fence` handle.
pub struct Fence {
device: Arc<Device>,
fence: vk::Fence,
}
impl Fence {
/// Creates a new `Fence`.
///
/// # Arguments
/// * `device` - The logical device.
/// * `signaled` - If true, the fence is created in the signaled state.
pub fn new(device: Arc<Device>, signaled: bool) -> Result<Self> {
let create_flags = if signaled {
vk::FenceCreateFlags::SIGNALED
} else {
vk::FenceCreateFlags::empty()
};
let create_info = vk::FenceCreateInfo::default().flags(create_flags);
let fence = unsafe { device.raw().create_fence(&create_info, None)? };
tracing::trace!("Created Fence (signaled: {})", signaled);
Ok(Self { device, fence })
}
/// Waits for the fence to become signaled.
///
/// # Arguments
/// * `timeout` - Maximum duration to wait. `None` waits indefinitely.
pub fn wait(&self, timeout: Option<Duration>) -> Result<()> {
let timeout_ns = timeout.map_or(u64::MAX, |d| d.as_nanos() as u64);
tracing::trace!("Waiting for Fence with timeout: {:?}", timeout);
let fences = [self.fence];
match unsafe { self.device.raw().wait_for_fences(&fences, true, timeout_ns) } {
Ok(_) => {
tracing::trace!("Fence signaled.");
Ok(())
}
Err(vk::Result::TIMEOUT) => {
tracing::trace!("Fence wait timed out.");
Err(GfxHalError::VulkanError(vk::Result::TIMEOUT)) // Return timeout error
}
Err(e) => Err(GfxHalError::VulkanError(e)),
}
}
/// Resets the fence to the unsignaled state.
/// Must only be called when the fence is not in use by pending GPU work.
pub fn reset(&self) -> Result<()> {
tracing::trace!("Resetting Fence.");
let fences = [self.fence];
unsafe { self.device.raw().reset_fences(&fences)? };
Ok(())
}
/// Checks the current status of the fence without waiting.
/// Returns `Ok(true)` if signaled, `Ok(false)` if unsignaled.
pub fn status(&self) -> Result<bool> {
match unsafe { self.device.raw().get_fence_status(self.fence) } {
Ok(signaled) => Ok(signaled),
// NOT_READY means unsignaled, not an error in this context
Err(vk::Result::NOT_READY) => Ok(false),
Err(e) => Err(GfxHalError::VulkanError(e)),
}
}
/// Gets the raw `vk::Fence` handle.
pub fn handle(&self) -> vk::Fence {
self.fence
}
}
impl Drop for Fence {
fn drop(&mut self) {
tracing::trace!("Destroying fence...");
unsafe {
self.device.raw().destroy_fence(self.fence, None);
}
tracing::trace!("Fence destroyed.")
}
}
/// Wraps a `vk::Semaphore`, used for GPU-GPU synchronization (within or across queues).
///
/// Owns the `vk::Semaphore` handle.
pub struct Semaphore {
device: Arc<Device>,
semaphore: vk::Semaphore,
}
impl Semaphore {
/// Creates a new `Semaphore`.
pub fn new(device: Arc<Device>) -> Result<Self> {
let create_info = vk::SemaphoreCreateInfo::default();
let semaphore = unsafe { device.raw().create_semaphore(&create_info, None)? };
tracing::trace!("Created Semaphore.");
Ok(Self { device, semaphore })
}
/// Gets the raw `vk::Semaphore` handle.
pub fn handle(&self) -> vk::Semaphore {
self.semaphore
}
}
impl Drop for Semaphore {
fn drop(&mut self) {
tracing::trace!("Destroying Semaphore...");
unsafe {
self.device.raw().destroy_semaphore(self.semaphore, None);
}
tracing::trace!("Semaphore destroyed.");
}
}
/// Wraps a `vk::Event`, used for fine-grained GPU-GPU or GPU-Host synchronization.
///
/// Owns the `vk::Event` handle.
pub struct Event {
device: Arc<Device>,
event: vk::Event,
}
impl Event {
/// Creates a new `Event`.
pub fn new(device: Arc<Device>) -> Result<Self> {
let create_info = vk::EventCreateInfo::default();
let event = unsafe { device.raw().create_event(&create_info, None)? };
tracing::trace!("Created Event.");
Ok(Self { device, event })
}
/// Sets the event from the host (CPU).
pub fn set(&self) -> Result<()> {
tracing::trace!("Setting Event from host.");
unsafe { self.device.raw().set_event(self.event)? };
Ok(())
}
/// Resets the event from the host (CPU).
pub fn reset(&self) -> Result<()> {
tracing::trace!("Resetting Event from host.");
unsafe { self.device.raw().reset_event(self.event)? };
Ok(())
}
/// Checks the status of the event from the host (CPU).
/// Returns `Ok(true)` if set, `Ok(false)` if reset.
pub fn status(&self) -> Result<bool> {
let res = unsafe { self.device.raw().get_event_status(self.event) }?;
Ok(res)
}
/// Gets the raw `vk::Event` handle.
pub fn handle(&self) -> vk::Event {
self.event
}
// Note: Setting/resetting/waiting on events from the GPU involves
// vkCmdSetEvent, vkCmdResetEvent, vkCmdWaitEvents within command buffers.
// These are not wrapped here but would be used via device.raw() when
// recording command buffers.
}
impl Drop for Event {
fn drop(&mut self) {
tracing::trace!("Destroying Event...");
unsafe {
self.device.raw().destroy_event(self.event, None);
}
tracing::trace!("Event destroyed.");
}
}