blob: 8f8bc3326700f8016545d54a889af2684318e1d4 [file] [log] [blame] [edit]
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! An event provides fine-grained synchronization within a single queue, or from the host to a
//! queue.
//!
//! When an event is signaled from a queue using the [`set_event`] command buffer command,
//! an event acts similar to a [pipeline barrier], but the synchronization scopes are split:
//! the source synchronization scope includes only commands before the `set_event` command,
//! while the destination synchronization scope includes only commands after the
//! [`wait_events`] command. Commands in between the two are not included.
//!
//! An event can also be signaled from the host, by calling the [`set`] method directly on the
//! [`Event`].
//!
//! [`set_event`]: crate::command_buffer::CommandBufferBuilder::set_event
//! [pipeline barrier]: crate::command_buffer::CommandBufferBuilder::pipeline_barrier
//! [`wait_events`]: crate::command_buffer::CommandBufferBuilder::wait_events
//! [`set`]: Event::set
use crate::{
device::{Device, DeviceOwned},
macros::impl_id_counter,
OomError, RequiresOneOf, VulkanError, VulkanObject,
};
use std::{
error::Error,
fmt::{Display, Error as FmtError, Formatter},
mem::MaybeUninit,
num::NonZeroU64,
ptr,
sync::Arc,
};
/// Used to block the GPU execution until an event on the CPU occurs.
///
/// Note that Vulkan implementations may have limits on how long a command buffer will wait for an
/// event to be signaled, in order to avoid interfering with progress of other clients of the GPU.
/// If the event isn't signaled within these limits, results are undefined and may include
/// device loss.
#[derive(Debug)]
pub struct Event {
handle: ash::vk::Event,
device: Arc<Device>,
id: NonZeroU64,
must_put_in_pool: bool,
}
impl Event {
/// Creates a new `Event`.
///
/// On [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag)
/// devices, the
/// [`events`](crate::device::Features::events)
/// feature must be enabled on the device.
#[inline]
pub fn new(device: Arc<Device>, _create_info: EventCreateInfo) -> Result<Event, EventError> {
// VUID-vkCreateEvent-events-04468
if device.enabled_extensions().khr_portability_subset && !device.enabled_features().events {
return Err(EventError::RequirementNotMet {
required_for: "this device is a portability subset device, and `Event::new` was \
called",
requires_one_of: RequiresOneOf {
features: &["events"],
..Default::default()
},
});
}
let create_info = ash::vk::EventCreateInfo {
flags: ash::vk::EventCreateFlags::empty(),
..Default::default()
};
let handle = unsafe {
let mut output = MaybeUninit::uninit();
let fns = device.fns();
(fns.v1_0.create_event)(
device.handle(),
&create_info,
ptr::null(),
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
Ok(Event {
handle,
device,
id: Self::next_id(),
must_put_in_pool: false,
})
}
/// Takes an event from the vulkano-provided event pool.
/// If the pool is empty, a new event will be allocated.
/// Upon `drop`, the event is put back into the pool.
///
/// For most applications, using the event pool should be preferred,
/// in order to avoid creating new events every frame.
#[inline]
pub fn from_pool(device: Arc<Device>) -> Result<Event, EventError> {
let handle = device.event_pool().lock().pop();
let event = match handle {
Some(handle) => {
unsafe {
// Make sure the event isn't signaled
let fns = device.fns();
(fns.v1_0.reset_event)(device.handle(), handle)
.result()
.map_err(VulkanError::from)?;
}
Event {
handle,
device,
id: Self::next_id(),
must_put_in_pool: true,
}
}
None => {
// Pool is empty, alloc new event
let mut event = Event::new(device, Default::default())?;
event.must_put_in_pool = true;
event
}
};
Ok(event)
}
/// Creates a new `Event` from a raw object handle.
///
/// # Safety
///
/// - `handle` must be a valid Vulkan object handle created from `device`.
/// - `create_info` must match the info used to create the object.
#[inline]
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::Event,
_create_info: EventCreateInfo,
) -> Event {
Event {
handle,
device,
id: Self::next_id(),
must_put_in_pool: false,
}
}
/// Returns true if the event is signaled.
#[inline]
pub fn signaled(&self) -> Result<bool, OomError> {
unsafe {
let fns = self.device.fns();
let result = (fns.v1_0.get_event_status)(self.device.handle(), self.handle);
match result {
ash::vk::Result::EVENT_SET => Ok(true),
ash::vk::Result::EVENT_RESET => Ok(false),
err => Err(VulkanError::from(err).into()),
}
}
}
/// See the docs of set().
#[inline]
pub fn set_raw(&mut self) -> Result<(), OomError> {
unsafe {
let fns = self.device.fns();
(fns.v1_0.set_event)(self.device.handle(), self.handle)
.result()
.map_err(VulkanError::from)?;
Ok(())
}
}
/// Changes the `Event` to the signaled state.
///
/// If a command buffer is waiting on this event, it is then unblocked.
///
/// # Panics
///
/// - Panics if the device or host ran out of memory.
#[inline]
pub fn set(&mut self) {
self.set_raw().unwrap();
}
/// See the docs of reset().
#[inline]
pub fn reset_raw(&mut self) -> Result<(), OomError> {
unsafe {
let fns = self.device.fns();
(fns.v1_0.reset_event)(self.device.handle(), self.handle)
.result()
.map_err(VulkanError::from)?;
Ok(())
}
}
/// Changes the `Event` to the unsignaled state.
///
/// # Panics
///
/// - Panics if the device or host ran out of memory.
#[inline]
pub fn reset(&mut self) {
self.reset_raw().unwrap();
}
}
impl Drop for Event {
#[inline]
fn drop(&mut self) {
unsafe {
if self.must_put_in_pool {
let raw_event = self.handle;
self.device.event_pool().lock().push(raw_event);
} else {
let fns = self.device.fns();
(fns.v1_0.destroy_event)(self.device.handle(), self.handle, ptr::null());
}
}
}
}
unsafe impl VulkanObject for Event {
type Handle = ash::vk::Event;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl DeviceOwned for Event {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl_id_counter!(Event);
/// Parameters to create a new `Event`.
#[derive(Clone, Debug)]
pub struct EventCreateInfo {
pub _ne: crate::NonExhaustive,
}
impl Default for EventCreateInfo {
#[inline]
fn default() -> Self {
Self {
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EventError {
/// Not enough memory available.
OomError(OomError),
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
}
impl Error for EventError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::OomError(err) => Some(err),
_ => None,
}
}
}
impl Display for EventError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::OomError(_) => write!(f, "not enough memory available"),
Self::RequirementNotMet {
required_for,
requires_one_of,
} => write!(
f,
"a requirement was not met for: {}; requires one of: {}",
required_for, requires_one_of,
),
}
}
}
impl From<VulkanError> for EventError {
fn from(err: VulkanError) -> Self {
match err {
e @ VulkanError::OutOfHostMemory | e @ VulkanError::OutOfDeviceMemory => {
Self::OomError(e.into())
}
_ => panic!("unexpected error: {:?}", err),
}
}
}
#[cfg(test)]
mod tests {
use crate::{sync::event::Event, VulkanObject};
#[test]
fn event_create() {
let (device, _) = gfx_dev_and_queue!();
let event = Event::new(device, Default::default()).unwrap();
assert!(!event.signaled().unwrap());
}
#[test]
fn event_set() {
let (device, _) = gfx_dev_and_queue!();
let mut event = Event::new(device, Default::default()).unwrap();
assert!(!event.signaled().unwrap());
event.set();
assert!(event.signaled().unwrap());
}
#[test]
fn event_reset() {
let (device, _) = gfx_dev_and_queue!();
let mut event = Event::new(device, Default::default()).unwrap();
event.set();
assert!(event.signaled().unwrap());
event.reset();
assert!(!event.signaled().unwrap());
}
#[test]
fn event_pool() {
let (device, _) = gfx_dev_and_queue!();
assert_eq!(device.event_pool().lock().len(), 0);
let event1_internal_obj = {
let event = Event::from_pool(device.clone()).unwrap();
assert_eq!(device.event_pool().lock().len(), 0);
event.handle()
};
assert_eq!(device.event_pool().lock().len(), 1);
let event2 = Event::from_pool(device.clone()).unwrap();
assert_eq!(device.event_pool().lock().len(), 0);
assert_eq!(event2.handle(), event1_internal_obj);
}
}