diff --git a/vulkano/src/command_buffer/mod.rs b/vulkano/src/command_buffer/mod.rs index dfb0239b..1b90a821 100644 --- a/vulkano/src/command_buffer/mod.rs +++ b/vulkano/src/command_buffer/mod.rs @@ -128,7 +128,10 @@ use crate::{ query::{QueryControlFlags, QueryPipelineStatisticFlags}, range_map::RangeMap, render_pass::{Framebuffer, Subpass}, - sync::{semaphore::Semaphore, PipelineStageAccessFlags, PipelineStages}, + sync::{ + semaphore::{Semaphore, SemaphoreType}, + PipelineStageAccessFlags, PipelineStages, + }, DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, }; use ahash::HashMap; @@ -774,6 +777,7 @@ impl SubmitInfo { let &SemaphoreSubmitInfo { semaphore: _, + value: _, stages, _ne: _, } = semaphore_submit_info; @@ -791,6 +795,11 @@ impl SubmitInfo { } } + // unsafe + // VUID-VkSubmitInfo2-semaphore-03882 + // VUID-VkSubmitInfo2-semaphore-03883 + // VUID-VkSubmitInfo2-semaphore-03884 + Ok(()) } } @@ -837,6 +846,18 @@ pub struct SemaphoreSubmitInfo { /// There is no default value. pub semaphore: Arc, + /// If `semaphore.semaphore_type()` is [`SemaphoreType::Timeline`], specifies the value that + /// will be used for the semaphore operation: + /// - If it's a signal operation, then the semaphore's value will be set to this value + /// when it is signaled. + /// - If it's a wait operation, then the semaphore will wait until its value is greater than + /// or equal to this value. + /// + /// If `semaphore.semaphore_type()` is [`SemaphoreType::Binary`], then this must be `0`. + /// + /// The default value is `0`. + pub value: u64, + /// For a semaphore wait operation, specifies the pipeline stages in the second synchronization /// scope: stages of queue operations following the wait operation that can start executing /// after the semaphore is signalled. @@ -862,6 +883,7 @@ impl SemaphoreSubmitInfo { pub fn new(semaphore: Arc) -> Self { Self { semaphore, + value: 0, stages: PipelineStages::ALL_COMMANDS, _ne: crate::NonExhaustive(()), } @@ -870,6 +892,7 @@ impl SemaphoreSubmitInfo { pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { let &Self { ref semaphore, + value, stages, _ne: _, } = self; @@ -877,6 +900,20 @@ impl SemaphoreSubmitInfo { // VUID? assert_eq!(device, semaphore.device().as_ref()); + match semaphore.semaphore_type() { + SemaphoreType::Binary => { + if value != 0 { + return Err(Box::new(ValidationError { + problem: "`semaphore.semaphore_type()` is `SemaphoreType::Binary`, but \ + `value` is not `0`" + .into(), + ..Default::default() + })); + } + } + SemaphoreType::Timeline => {} + } + stages.validate_device(device).map_err(|err| { err.add_context("stages") .set_vuids(&["VUID-VkSemaphoreSubmitInfo-stageMask-parameter"]) diff --git a/vulkano/src/device/physical.rs b/vulkano/src/device/physical.rs index 1e45249d..fecb16c6 100644 --- a/vulkano/src/device/physical.rs +++ b/vulkano/src/device/physical.rs @@ -27,7 +27,7 @@ use crate::{ }, sync::{ fence::{ExternalFenceInfo, ExternalFenceProperties}, - semaphore::{ExternalSemaphoreInfo, ExternalSemaphoreProperties}, + semaphore::{ExternalSemaphoreInfo, ExternalSemaphoreProperties, SemaphoreType}, Sharing, }, DebugWrapper, ExtensionProperties, Requires, RequiresAllOf, RequiresOneOf, Validated, @@ -1191,13 +1191,28 @@ impl PhysicalDevice { let &ExternalSemaphoreInfo { handle_type, + semaphore_type, + initial_value, _ne: _, } = info; - let external_semaphore_info = ash::vk::PhysicalDeviceExternalSemaphoreInfo { + let mut external_semaphore_info_vk = ash::vk::PhysicalDeviceExternalSemaphoreInfo { handle_type: handle_type.into(), ..Default::default() }; + let mut semaphore_type_create_info_vk = None; + + if semaphore_type != SemaphoreType::Binary { + let next = + semaphore_type_create_info_vk.insert(ash::vk::SemaphoreTypeCreateInfo { + semaphore_type: semaphore_type.into(), + initial_value, + ..Default::default() + }); + + next.p_next = external_semaphore_info_vk.p_next; + external_semaphore_info_vk.p_next = next as *const _ as *const _; + } /* Output */ @@ -1211,14 +1226,14 @@ impl PhysicalDevice { if self.instance.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_external_semaphore_properties)( self.handle, - &external_semaphore_info, + &external_semaphore_info_vk, &mut external_semaphore_properties, ) } else { (fns.khr_external_semaphore_capabilities .get_physical_device_external_semaphore_properties_khr)( self.handle, - &external_semaphore_info, + &external_semaphore_info_vk, &mut external_semaphore_properties, ); } diff --git a/vulkano/src/device/queue.rs b/vulkano/src/device/queue.rs index e6659028..38039314 100644 --- a/vulkano/src/device/queue.rs +++ b/vulkano/src/device/queue.rs @@ -16,7 +16,7 @@ use crate::{ BindSparseInfo, SparseBufferMemoryBind, SparseImageMemoryBind, SparseImageOpaqueMemoryBind, }, swapchain::{PresentInfo, SemaphorePresentInfo, SwapchainPresentInfo}, - sync::{fence::Fence, PipelineStages}, + sync::{fence::Fence, semaphore::SemaphoreType, PipelineStages}, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError, VulkanObject, }; @@ -778,6 +778,7 @@ impl<'a> QueueGuard<'a> { for (semaphore_index, semaphore_submit_info) in wait_semaphores.iter().enumerate() { let &SemaphoreSubmitInfo { semaphore: _, + value: _, stages, _ne: _, } = semaphore_submit_info; @@ -824,6 +825,7 @@ impl<'a> QueueGuard<'a> { for (semaphore_index, semaphore_submit_info) in signal_semaphores.iter().enumerate() { let &SemaphoreSubmitInfo { semaphore: _, + value: _, stages, _ne: _, } = semaphore_submit_info; @@ -846,12 +848,14 @@ impl<'a> QueueGuard<'a> { } // unsafe + // VUID-VkSubmitInfo2-semaphore-03882 + // VUID-VkSubmitInfo2-semaphore-03883 + // VUID-VkSubmitInfo2-semaphore-03884 // VUID-vkQueueSubmit2-fence-04894 // VUID-vkQueueSubmit2-fence-04895 // VUID-vkQueueSubmit2-commandBuffer-03867 // VUID-vkQueueSubmit2-semaphore-03868 // VUID-vkQueueSubmit2-semaphore-03871 - // VUID-vkQueueSubmit2-semaphore-03872 // VUID-vkQueueSubmit2-semaphore-03873 // VUID-vkQueueSubmit2-commandBuffer-03874 // VUID-vkQueueSubmit2-commandBuffer-03875 @@ -875,7 +879,7 @@ impl<'a> QueueGuard<'a> { signal_semaphore_infos_vk: SmallVec<[ash::vk::SemaphoreSubmitInfo; 4]>, } - let (mut submit_info_vk, per_submit_vk): (SmallVec<[_; 4]>, SmallVec<[_; 4]>) = + let (mut submit_info_vk, mut per_submit_vk): (SmallVec<[_; 4]>, SmallVec<[_; 4]>) = submit_infos .iter() .map(|submit_info| { @@ -886,59 +890,65 @@ impl<'a> QueueGuard<'a> { _ne: _, } = submit_info; - let wait_semaphore_infos_vk = wait_semaphores - .iter() - .map(|semaphore_submit_info| { - let &SemaphoreSubmitInfo { - ref semaphore, - stages, - _ne: _, - } = semaphore_submit_info; + let mut per_submit_vk = PerSubmitInfo { + wait_semaphore_infos_vk: SmallVec::with_capacity(wait_semaphores.len()), + command_buffer_infos_vk: SmallVec::with_capacity(command_buffers.len()), + signal_semaphore_infos_vk: SmallVec::with_capacity( + signal_semaphores.len(), + ), + }; + let PerSubmitInfo { + wait_semaphore_infos_vk, + command_buffer_infos_vk, + signal_semaphore_infos_vk, + } = &mut per_submit_vk; - ash::vk::SemaphoreSubmitInfo { - semaphore: semaphore.handle(), - value: 0, // TODO: - stage_mask: stages.into(), - device_index: 0, // TODO: - ..Default::default() - } - }) - .collect(); + for semaphore_submit_info in wait_semaphores { + let &SemaphoreSubmitInfo { + ref semaphore, + value, + stages, + _ne: _, + } = semaphore_submit_info; - let command_buffer_infos_vk = command_buffers - .iter() - .map(|command_buffer_submit_info| { - let &CommandBufferSubmitInfo { - ref command_buffer, - _ne: _, - } = command_buffer_submit_info; + wait_semaphore_infos_vk.push(ash::vk::SemaphoreSubmitInfo { + semaphore: semaphore.handle(), + value, + stage_mask: stages.into(), + device_index: 0, // TODO: + ..Default::default() + }); + } - ash::vk::CommandBufferSubmitInfo { - command_buffer: command_buffer.handle(), - device_mask: 0, // TODO: - ..Default::default() - } - }) - .collect(); + for command_buffer_submit_info in command_buffers { + let &CommandBufferSubmitInfo { + ref command_buffer, + _ne: _, + } = command_buffer_submit_info; - let signal_semaphore_infos_vk = signal_semaphores - .iter() - .map(|semaphore_submit_info| { - let &SemaphoreSubmitInfo { - ref semaphore, - stages, - _ne: _, - } = semaphore_submit_info; + command_buffer_infos_vk.push(ash::vk::CommandBufferSubmitInfo { + command_buffer: command_buffer.handle(), + device_mask: 0, // TODO: + ..Default::default() + }); + } - ash::vk::SemaphoreSubmitInfo { - semaphore: semaphore.handle(), - value: 0, // TODO: - stage_mask: stages.into(), - device_index: 0, // TODO: - ..Default::default() - } - }) - .collect(); + for semaphore_submit_info in signal_semaphores { + let &SemaphoreSubmitInfo { + ref semaphore, + value, + stages, + _ne: _, + } = semaphore_submit_info; + + signal_semaphore_infos_vk.push(ash::vk::SemaphoreSubmitInfo { + semaphore: semaphore.handle(), + value, + stage_mask: stages.into(), + device_index: 0, // TODO: + ..Default::default() + }); + } ( ash::vk::SubmitInfo2 { @@ -951,11 +961,7 @@ impl<'a> QueueGuard<'a> { p_signal_semaphore_infos: ptr::null(), ..Default::default() }, - PerSubmitInfo { - wait_semaphore_infos_vk, - command_buffer_infos_vk, - signal_semaphore_infos_vk, - }, + per_submit_vk, ) }) .unzip(); @@ -967,7 +973,7 @@ impl<'a> QueueGuard<'a> { command_buffer_infos_vk, signal_semaphore_infos_vk, }, - ) in (submit_info_vk.iter_mut()).zip(per_submit_vk.iter()) + ) in (submit_info_vk.iter_mut()).zip(per_submit_vk.iter_mut()) { *submit_info_vk = ash::vk::SubmitInfo2 { wait_semaphore_info_count: wait_semaphore_infos_vk.len() as u32, @@ -1006,13 +1012,16 @@ impl<'a> QueueGuard<'a> { .map_err(VulkanError::from) } else { struct PerSubmitInfo { + timeline_semaphore_submit_info_vk: Option, wait_semaphores_vk: SmallVec<[ash::vk::Semaphore; 4]>, + wait_semaphore_values_vk: SmallVec<[u64; 4]>, wait_dst_stage_mask_vk: SmallVec<[ash::vk::PipelineStageFlags; 4]>, command_buffers_vk: SmallVec<[ash::vk::CommandBuffer; 4]>, signal_semaphores_vk: SmallVec<[ash::vk::Semaphore; 4]>, + signal_semaphore_values_vk: SmallVec<[u64; 4]>, } - let (mut submit_info_vk, per_submit_vk): (SmallVec<[_; 4]>, SmallVec<[_; 4]>) = + let (mut submit_info_vk, mut per_submit_vk): (SmallVec<[_; 4]>, SmallVec<[_; 4]>) = submit_infos .iter() .map(|submit_info| { @@ -1023,43 +1032,77 @@ impl<'a> QueueGuard<'a> { _ne: _, } = submit_info; - let (wait_semaphores_vk, wait_dst_stage_mask_vk) = wait_semaphores - .iter() - .map(|semaphore_submit_info| { - let &SemaphoreSubmitInfo { - ref semaphore, - stages, - _ne: _, - } = semaphore_submit_info; + let mut per_submit_vk = PerSubmitInfo { + timeline_semaphore_submit_info_vk: None, + wait_semaphores_vk: SmallVec::with_capacity(wait_semaphores.len()), + wait_semaphore_values_vk: SmallVec::with_capacity( + wait_semaphores.len(), + ), + wait_dst_stage_mask_vk: SmallVec::with_capacity(wait_semaphores.len()), + command_buffers_vk: SmallVec::with_capacity(command_buffers.len()), + signal_semaphores_vk: SmallVec::with_capacity(signal_semaphores.len()), + signal_semaphore_values_vk: SmallVec::with_capacity( + signal_semaphores.len(), + ), + }; + let PerSubmitInfo { + timeline_semaphore_submit_info_vk, + wait_semaphores_vk, + wait_semaphore_values_vk, + wait_dst_stage_mask_vk, + command_buffers_vk, + signal_semaphores_vk, + signal_semaphore_values_vk, + } = &mut per_submit_vk; - (semaphore.handle(), stages.into()) - }) - .unzip(); + let mut has_timeline_semaphores = false; - let command_buffers_vk = command_buffers - .iter() - .map(|command_buffer_submit_info| { - let &CommandBufferSubmitInfo { - ref command_buffer, - _ne: _, - } = command_buffer_submit_info; + for semaphore_submit_info in wait_semaphores { + let &SemaphoreSubmitInfo { + ref semaphore, + value, + stages, + _ne: _, + } = semaphore_submit_info; - command_buffer.handle() - }) - .collect(); + if semaphore.semaphore_type() == SemaphoreType::Timeline { + has_timeline_semaphores = true; + } - let signal_semaphores_vk = signal_semaphores - .iter() - .map(|semaphore_submit_info| { - let &SemaphoreSubmitInfo { - ref semaphore, - stages: _, - _ne: _, - } = semaphore_submit_info; + wait_semaphores_vk.push(semaphore.handle()); + wait_semaphore_values_vk.push(value); + wait_dst_stage_mask_vk.push(stages.into()); + } - semaphore.handle() - }) - .collect(); + for command_buffer_submit_info in command_buffers { + let &CommandBufferSubmitInfo { + ref command_buffer, + _ne: _, + } = command_buffer_submit_info; + + command_buffers_vk.push(command_buffer.handle()); + } + + for semaphore_submit_info in signal_semaphores { + let &SemaphoreSubmitInfo { + ref semaphore, + value, + stages: _, + _ne: _, + } = semaphore_submit_info; + + if semaphore.semaphore_type() == SemaphoreType::Timeline { + has_timeline_semaphores = true; + } + + signal_semaphores_vk.push(semaphore.handle()); + signal_semaphore_values_vk.push(value); + } + + if has_timeline_semaphores { + *timeline_semaphore_submit_info_vk = + Some(ash::vk::TimelineSemaphoreSubmitInfo::default()); + } ( ash::vk::SubmitInfo { @@ -1072,12 +1115,7 @@ impl<'a> QueueGuard<'a> { p_signal_semaphores: ptr::null(), ..Default::default() }, - PerSubmitInfo { - wait_semaphores_vk, - wait_dst_stage_mask_vk, - command_buffers_vk, - signal_semaphores_vk, - }, + per_submit_vk, ) }) .unzip(); @@ -1085,12 +1123,15 @@ impl<'a> QueueGuard<'a> { for ( submit_info_vk, PerSubmitInfo { + timeline_semaphore_submit_info_vk, wait_semaphores_vk, + wait_semaphore_values_vk, wait_dst_stage_mask_vk, command_buffers_vk, signal_semaphores_vk, + signal_semaphore_values_vk, }, - ) in (submit_info_vk.iter_mut()).zip(per_submit_vk.iter()) + ) in (submit_info_vk.iter_mut()).zip(per_submit_vk.iter_mut()) { *submit_info_vk = ash::vk::SubmitInfo { wait_semaphore_count: wait_semaphores_vk.len() as u32, @@ -1102,6 +1143,19 @@ impl<'a> QueueGuard<'a> { p_signal_semaphores: signal_semaphores_vk.as_ptr(), ..*submit_info_vk }; + + if let Some(timeline_semaphore_submit_info_vk) = timeline_semaphore_submit_info_vk { + *timeline_semaphore_submit_info_vk = ash::vk::TimelineSemaphoreSubmitInfo { + wait_semaphore_value_count: wait_semaphore_values_vk.len() as u32, + p_wait_semaphore_values: wait_semaphore_values_vk.as_ptr(), + signal_semaphore_value_count: signal_semaphore_values_vk.len() as u32, + p_signal_semaphore_values: signal_semaphore_values_vk.as_ptr(), + ..*timeline_semaphore_submit_info_vk + }; + + timeline_semaphore_submit_info_vk.p_next = submit_info_vk.p_next; + submit_info_vk.p_next = timeline_semaphore_submit_info_vk as *mut _ as *mut _; + } } let fns = self.queue.device.fns(); diff --git a/vulkano/src/swapchain/acquire_present.rs b/vulkano/src/swapchain/acquire_present.rs index 6f605bea..33e4fb47 100644 --- a/vulkano/src/swapchain/acquire_present.rs +++ b/vulkano/src/swapchain/acquire_present.rs @@ -15,7 +15,7 @@ use crate::{ sync::{ fence::Fence, future::{queue_present, AccessCheckError, AccessError, GpuFuture, SubmitAnyBuilder}, - semaphore::Semaphore, + semaphore::{Semaphore, SemaphoreType}, }, DeviceSize, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, VulkanError, VulkanObject, @@ -104,6 +104,15 @@ impl AcquireNextImageInfo { if let Some(semaphore) = semaphore { // VUID-VkAcquireNextImageInfoKHR-commonparent assert_eq!(device, semaphore.device().as_ref()); + + if semaphore.semaphore_type() != SemaphoreType::Binary { + return Err(Box::new(ValidationError { + context: "semaphore.semaphore_type()".into(), + problem: "is not `SemaphoreType::Binary`".into(), + vuids: &["VUID-VkAcquireNextImageInfoKHR-semaphore-03266"], + ..Default::default() + })); + } } if let Some(fence) = fence { @@ -757,6 +766,15 @@ impl SemaphorePresentInfo { // VUID-VkPresentInfoKHR-commonparent assert_eq!(device, semaphore.device().as_ref()); + if semaphore.semaphore_type() != SemaphoreType::Binary { + return Err(Box::new(ValidationError { + context: "semaphore.semaphore_type()".into(), + problem: "is not `SemaphoreType::Binary`".into(), + vuids: &["VUID-vkQueuePresentKHR-pWaitSemaphores-03267"], + ..Default::default() + })); + } + Ok(()) } } diff --git a/vulkano/src/sync/event.rs b/vulkano/src/sync/event.rs index e7bb2d51..2eeda99e 100644 --- a/vulkano/src/sync/event.rs +++ b/vulkano/src/sync/event.rs @@ -19,9 +19,9 @@ //! 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`]: crate::command_buffer::sys::UnsafeCommandBufferBuilder::set_event +//! [pipeline barrier]: crate::command_buffer::sys::UnsafeCommandBufferBuilder::pipeline_barrier +//! [`wait_events`]: crate::command_buffer::sys::UnsafeCommandBufferBuilder::wait_events //! [`set`]: Event::set use crate::{ diff --git a/vulkano/src/sync/semaphore.rs b/vulkano/src/sync/semaphore.rs index dec0a019..c9af330c 100644 --- a/vulkano/src/sync/semaphore.rs +++ b/vulkano/src/sync/semaphore.rs @@ -10,24 +10,48 @@ //! A semaphore provides synchronization between multiple queues, with non-command buffer //! commands on the same queue, or between the device and an external source. //! -//! A semaphore has two states: **signaled** and **unsignaled**. -//! Only the device can perform operations on a semaphore, +//! Semaphores come in two types: **binary** and **timeline** semaphores. +//! +//! # Binary semaphores +//! +//! A binary semaphore has two states: **signaled** and **unsignaled**. +//! Only the device can perform operations on a binary semaphore, //! the host cannot perform any operations on it. //! -//! Two operations can be performed on a semaphore: +//! Two operations can be performed on a binary semaphore: //! - A **semaphore signal operation** will put the semaphore into the signaled state. -//! - A **semaphore wait operation** will block execution of the operation is associated with, +//! - A **semaphore wait operation** will block execution of the operation it is associated with, //! as long as the semaphore is in the unsignaled state. Once the semaphore is in the signaled //! state, the semaphore is put back in the unsignaled state and execution continues. //! -//! Semaphore signals and waits must always occur in pairs: one signal operation is paired with one -//! wait operation. If a semaphore is signaled without waiting for it, it stays in the signaled -//! state until it is waited for, or destroyed. +//! Binary semaphore signals and waits must always occur in pairs: one signal operation is paired +//! with one wait operation. If a binary semaphore is signaled without waiting for it, it stays in +//! the signaled state until it is waited for, or destroyed. +//! +//! # Timeline semaphores +//! +//! Also called *counting semaphore* in literature, its state is an integer counter value. +//! Timeline semaphores cannot be used in swapchain-related commands, +//! binary semaphores and fences must be used. +//! +//! Both the device and the host can perform the same two operations on a timeline semaphore: +//! - A **semaphore signal operation** will set the semaphore counter value to a specified value. +//! - A **semaphore wait operation** will block execution of the operation it is associated with, +//! as long as the semaphore's counter value is less than a specified threshold value. +//! Once the semaphore's counter value is equal to or greater than the threshold, execution +//! continues. Unlike with binary semaphores, waiting does not alter the state of a timeline +//! semaphore, so multiple operations can wait for the same semaphore value. +//! +//! Additionally, the host can query the current counter value of a timeline semaphore. +//! +//! If the device signals a timeline semaphore, and the host waits for it, then it can be used +//! in ways similar to a [fence], to signal to the host that the device has completed an +//! operation. //! //! # Safety //! -//! - When a semaphore signal operation is executed on the device, -//! the semaphore must be in the unsignaled state. +//! For binary semaphores: +//! - When a semaphore signal operation is executed, the semaphore must be in the unsignaled state. //! In other words, the same semaphore cannot be signalled by multiple commands; //! there must always be a wait operation in between them. //! - There must never be more than one semaphore wait operation executing on the same semaphore @@ -36,15 +60,29 @@ //! the semaphore must already be in the signaled state, or //! the signal operation that it waits for must have been queued previously //! (as part of a previous command, or an earlier batch within the same command). +//! +//! For timeline semaphores: +//! - When a semaphore signal operation is executed, the new counter value of the semaphore must be +//! greater than its current value, and less than the value of any pending signal operations on +//! that semaphore. +//! - If an operation both waits on and signals the same semaphore, the signaled value must be +//! greater than the waited value. +//! - At any given time, the difference between the current semaphore counter value, and the value +//! of any outstanding signal or wait operations on that semaphore, must not be greater than the +//! [`max_timeline_semaphore_value_difference`] device limit. +//! +//! [fence]: crate::sync::fence +//! [`max_timeline_semaphore_value_difference`]: crate::device::Properties::max_timeline_semaphore_value_difference use crate::{ device::{physical::PhysicalDevice, Device, DeviceOwned}, instance::InstanceOwnedDebugWrapper, - macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum}, + macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum, vulkan_enum}, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError, VulkanObject, }; -use std::{fs::File, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc}; +use smallvec::SmallVec; +use std::{fs::File, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc, time::Duration}; /// Used to provide synchronization between command buffers during their execution. /// @@ -56,6 +94,7 @@ pub struct Semaphore { device: InstanceOwnedDebugWrapper>, id: NonZeroU64, + semaphore_type: SemaphoreType, export_handle_types: ExternalSemaphoreHandleTypes, must_put_in_pool: bool, @@ -89,27 +128,39 @@ impl Semaphore { device: Arc, create_info: SemaphoreCreateInfo, ) -> Result { - let SemaphoreCreateInfo { + let &SemaphoreCreateInfo { + semaphore_type, + initial_value, export_handle_types, _ne: _, - } = create_info; + } = &create_info; let mut create_info_vk = ash::vk::SemaphoreCreateInfo { flags: ash::vk::SemaphoreCreateFlags::empty(), ..Default::default() }; + let mut semaphore_type_create_info_vk = None; let mut export_semaphore_create_info_vk = None; + if semaphore_type != SemaphoreType::Binary { + let next = semaphore_type_create_info_vk.insert(ash::vk::SemaphoreTypeCreateInfo { + semaphore_type: semaphore_type.into(), + initial_value, + ..Default::default() + }); + + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *const _ as *const _; + } + if !export_handle_types.is_empty() { - let _ = export_semaphore_create_info_vk.insert(ash::vk::ExportSemaphoreCreateInfo { + let next = export_semaphore_create_info_vk.insert(ash::vk::ExportSemaphoreCreateInfo { handle_types: export_handle_types.into(), ..Default::default() }); - }; - if let Some(info) = export_semaphore_create_info_vk.as_mut() { - info.p_next = create_info_vk.p_next; - create_info_vk.p_next = info as *const _ as *const _; + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *const _ as *const _; } let handle = { @@ -144,6 +195,7 @@ impl Semaphore { device: InstanceOwnedDebugWrapper(device), id: Self::next_id(), + semaphore_type: SemaphoreType::Binary, export_handle_types: ExternalSemaphoreHandleTypes::empty(), must_put_in_pool: true, @@ -173,6 +225,8 @@ impl Semaphore { create_info: SemaphoreCreateInfo, ) -> Semaphore { let SemaphoreCreateInfo { + semaphore_type, + initial_value: _, export_handle_types, _ne: _, } = create_info; @@ -182,18 +236,329 @@ impl Semaphore { device: InstanceOwnedDebugWrapper(device), id: Self::next_id(), + semaphore_type, export_handle_types, must_put_in_pool: false, } } + /// Returns the type of the semaphore. + #[inline] + pub fn semaphore_type(&self) -> SemaphoreType { + self.semaphore_type + } + /// Returns the handle types that can be exported from the semaphore. #[inline] pub fn export_handle_types(&self) -> ExternalSemaphoreHandleTypes { self.export_handle_types } + /// If `self` is a timeline semaphore, returns the current counter value of the semaphore. + /// + /// The returned value may be immediately out of date, if a signal operation on the semaphore + /// is pending on the device. + #[inline] + pub fn counter_value(&self) -> Result> { + self.validate_counter_value()?; + + unsafe { Ok(self.counter_value_unchecked()?) } + } + + fn validate_counter_value(&self) -> Result<(), Box> { + if self.semaphore_type != SemaphoreType::Timeline { + return Err(Box::new(ValidationError { + context: "self.semaphore_type()".into(), + problem: "is not `SemaphoreType::Timeline`".into(), + vuids: &["VUID-vkGetSemaphoreCounterValue-semaphore-03255"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn counter_value_unchecked(&self) -> Result { + let mut output = MaybeUninit::uninit(); + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_2 { + (fns.v1_2.get_semaphore_counter_value)( + self.device.handle(), + self.handle, + output.as_mut_ptr(), + ) + } else { + (fns.khr_timeline_semaphore.get_semaphore_counter_value_khr)( + self.device.handle(), + self.handle, + output.as_mut_ptr(), + ) + } + .result() + .map_err(VulkanError::from)?; + + Ok(output.assume_init()) + } + + /// If `self` is a timeline semaphore, performs a signal operation on the semaphore, setting + /// the new counter value to `value`. + /// + /// # Safety + /// + /// - The safety requirements for semaphores, as detailed in the module documentation, + /// must be followed. + #[inline] + pub unsafe fn signal( + &self, + signal_info: SemaphoreSignalInfo, + ) -> Result<(), Validated> { + self.validate_signal(&signal_info)?; + + Ok(self.signal_unchecked(signal_info)?) + } + + fn validate_signal( + &self, + signal_info: &SemaphoreSignalInfo, + ) -> Result<(), Box> { + if self.semaphore_type != SemaphoreType::Timeline { + return Err(Box::new(ValidationError { + context: "self.semaphore_type()".into(), + problem: "is not `SemaphoreType::Timeline`".into(), + vuids: &["VUID-VkSemaphoreSignalInfo-semaphore-03257"], + ..Default::default() + })); + } + + signal_info.validate(&self.device)?; + + // unsafe + // VUID-VkSemaphoreSignalInfo-value-03258 + // VUID-VkSemaphoreSignalInfo-value-03259 + // VUID-VkSemaphoreSignalInfo-value-03260 + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn signal_unchecked( + &self, + signal_info: SemaphoreSignalInfo, + ) -> Result<(), VulkanError> { + let &SemaphoreSignalInfo { value, _ne: _ } = &signal_info; + + let signal_info_vk = ash::vk::SemaphoreSignalInfo { + semaphore: self.handle, + value, + ..Default::default() + }; + + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_2 { + (fns.v1_2.signal_semaphore)(self.device.handle(), &signal_info_vk) + } else { + (fns.khr_timeline_semaphore.signal_semaphore_khr)(self.device.handle(), &signal_info_vk) + } + .result() + .map_err(VulkanError::from) + } + + /// If `self` is a timeline semaphore, performs a wait operation on the semaphore, blocking + /// until the counter value is equal to or greater than `wait_info.value`. + #[inline] + pub fn wait( + &self, + wait_info: SemaphoreWaitInfo, + timeout: Option, + ) -> Result<(), Validated> { + self.validate_wait(&wait_info, timeout)?; + + unsafe { Ok(self.wait_unchecked(wait_info, timeout)?) } + } + + fn validate_wait( + &self, + wait_info: &SemaphoreWaitInfo, + timeout: Option, + ) -> Result<(), Box> { + if self.semaphore_type != SemaphoreType::Timeline { + return Err(Box::new(ValidationError { + context: "self.semaphore_type()".into(), + problem: "is not `SemaphoreType::Timeline`".into(), + vuids: &["VUID-VkSemaphoreWaitInfo-pSemaphores-03256"], + ..Default::default() + })); + } + + wait_info + .validate(&self.device) + .map_err(|err| err.add_context("wait_info"))?; + + if let Some(timeout) = timeout { + if timeout.as_nanos() >= u64::MAX as u128 { + return Err(Box::new(ValidationError { + context: "timeout".into(), + problem: "is not less than `u64::MAX` nanoseconds".into(), + ..Default::default() + })); + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn wait_unchecked( + &self, + wait_info: SemaphoreWaitInfo, + timeout: Option, + ) -> Result<(), VulkanError> { + let &SemaphoreWaitInfo { + flags, + value, + _ne: _, + } = &wait_info; + + let semaphores_vk = [self.handle]; + let values_vk = [value]; + + let wait_info_vk = ash::vk::SemaphoreWaitInfo { + flags: flags.into(), + semaphore_count: 1, + p_semaphores: semaphores_vk.as_ptr(), + p_values: values_vk.as_ptr(), + ..Default::default() + }; + + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_2 { + (fns.v1_2.wait_semaphores)( + self.device.handle(), + &wait_info_vk, + timeout.map_or(u64::MAX, |duration| { + u64::try_from(duration.as_nanos()).unwrap() + }), + ) + } else { + (fns.khr_timeline_semaphore.wait_semaphores_khr)( + self.device.handle(), + &wait_info_vk, + timeout.map_or(u64::MAX, |duration| { + u64::try_from(duration.as_nanos()).unwrap() + }), + ) + } + .result() + .map_err(VulkanError::from) + } + + /// Waits for multiple timeline semaphores at once. + /// + /// # Panics + /// + /// - Panics if not all semaphores belong to the same device. + #[inline] + pub fn wait_multiple( + wait_info: SemaphoreWaitMultipleInfo, + timeout: Option, + ) -> Result<(), Validated> { + Self::validate_wait_multiple(&wait_info, timeout)?; + + unsafe { Ok(Self::wait_multiple_unchecked(wait_info, timeout)?) } + } + + fn validate_wait_multiple( + wait_info: &SemaphoreWaitMultipleInfo, + timeout: Option, + ) -> Result<(), Box> { + if let Some(timeout) = timeout { + if timeout.as_nanos() >= u64::MAX as u128 { + return Err(Box::new(ValidationError { + context: "timeout".into(), + problem: "is not less than `u64::MAX` nanoseconds".into(), + ..Default::default() + })); + } + } + + if wait_info.semaphores.is_empty() { + return Ok(()); + } + + let device = &wait_info.semaphores[0].semaphore.device; + wait_info + .validate(device) + .map_err(|err| err.add_context("wait_info"))?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn wait_multiple_unchecked( + wait_info: SemaphoreWaitMultipleInfo, + timeout: Option, + ) -> Result<(), VulkanError> { + let &SemaphoreWaitMultipleInfo { + flags, + ref semaphores, + _ne: _, + } = &wait_info; + + if semaphores.is_empty() { + return Ok(()); + } + + let mut semaphores_vk: SmallVec<[_; 8]> = SmallVec::with_capacity(semaphores.len()); + let mut values_vk: SmallVec<[_; 8]> = SmallVec::with_capacity(semaphores.len()); + + for value_info in semaphores { + let &SemaphoreWaitValueInfo { + ref semaphore, + value, + _ne: _, + } = value_info; + + semaphores_vk.push(semaphore.handle); + values_vk.push(value); + } + + let wait_info_vk = ash::vk::SemaphoreWaitInfo { + flags: flags.into(), + semaphore_count: semaphores_vk.len() as u32, + p_semaphores: semaphores_vk.as_ptr(), + p_values: values_vk.as_ptr(), + ..Default::default() + }; + + let device = &semaphores[0].semaphore.device; + let fns = device.fns(); + + if device.api_version() >= Version::V1_2 { + (fns.v1_2.wait_semaphores)( + device.handle(), + &wait_info_vk, + timeout.map_or(u64::MAX, |duration| { + u64::try_from(duration.as_nanos()).unwrap() + }), + ) + } else { + (fns.khr_timeline_semaphore.wait_semaphores_khr)( + device.handle(), + &wait_info_vk, + timeout.map_or(u64::MAX, |duration| { + u64::try_from(duration.as_nanos()).unwrap() + }), + ) + } + .result() + .map_err(VulkanError::from) + } + /// Exports the semaphore into a POSIX file descriptor. The caller owns the returned `File`. /// /// # Safety @@ -254,6 +619,16 @@ impl Semaphore { })); } + if handle_type.has_copy_transference() && self.semaphore_type != SemaphoreType::Binary { + return Err(Box::new(ValidationError { + problem: "`handle_type` has copy transference, but \ + `self.semaphore_type()` is not `SemaphoreType::Binary`" + .into(), + vuids: &["VUID-VkSemaphoreGetFdInfoKHR-handleType-03253"], + ..Default::default() + })); + } + Ok(()) } @@ -428,6 +803,15 @@ impl Semaphore { .set_vuids(&["VUID-VkSemaphoreGetZirconHandleInfoFUCHSIA-handleType-parameter"]) })?; + if self.semaphore_type != SemaphoreType::Binary { + return Err(Box::new(ValidationError { + context: "self.semaphore_type()".into(), + problem: "is not `SemaphoreType::Binary`".into(), + vuids: &["VUID-VkSemaphoreGetZirconHandleInfoFUCHSIA-semaphore-04763"], + ..Default::default() + })); + } + if !matches!(handle_type, ExternalSemaphoreHandleType::ZirconEvent) { return Err(Box::new(ValidationError { context: "handle_type".into(), @@ -512,6 +896,26 @@ impl Semaphore { .validate(&self.device) .map_err(|err| err.add_context("import_semaphore_fd_info"))?; + let &ImportSemaphoreFdInfo { + flags, + handle_type: _, + file: _, + _ne: _, + } = import_semaphore_fd_info; + + if self.semaphore_type == SemaphoreType::Timeline + && flags.intersects(SemaphoreImportFlags::TEMPORARY) + { + return Err(Box::new(ValidationError { + problem: "`self.semaphore_type()` is `SemaphoreType::Timeline`, but \ + `import_semaphore_fd_info.flags` contains \ + `SemaphoreImportFlags::TEMPORARY`" + .into(), + vuids: &["VUID-VkImportSemaphoreFdInfoKHR-flags-03323"], + ..Default::default() + })); + } + Ok(()) } @@ -597,6 +1001,26 @@ impl Semaphore { .validate(&self.device) .map_err(|err| err.add_context("import_semaphore_win32_handle_info"))?; + let &ImportSemaphoreWin32HandleInfo { + flags, + handle_type: _, + handle: _, + _ne: _, + } = import_semaphore_win32_handle_info; + + if self.semaphore_type == SemaphoreType::Timeline + && flags.intersects(SemaphoreImportFlags::TEMPORARY) + { + return Err(Box::new(ValidationError { + problem: "`self.semaphore_type()` is `SemaphoreType::Timeline`, but \ + `import_semaphore_win32_handle_info.flags` contains \ + `SemaphoreImportFlags::TEMPORARY`" + .into(), + vuids: &["VUID-VkImportSemaphoreWin32HandleInfoKHR-flags-03322"], + ..Default::default() + })); + } + Ok(()) } @@ -667,6 +1091,14 @@ impl Semaphore { .validate(&self.device) .map_err(|err| err.add_context("import_semaphore_zircon_handle_info"))?; + if self.semaphore_type == SemaphoreType::Timeline { + return Err(Box::new(ValidationError { + problem: "`self.semaphore_type()` is `SemaphoreType::Timeline`".into(), + vuids: &["VUID-VkImportSemaphoreZirconHandleInfoFUCHSIA-semaphoreType-04768"], + ..Default::default() + })); + } + Ok(()) } @@ -736,6 +1168,19 @@ impl_id_counter!(Semaphore); /// Parameters to create a new `Semaphore`. #[derive(Clone, Debug)] pub struct SemaphoreCreateInfo { + /// The type of semaphore to create. + /// + /// The default value is [`SemaphoreType::Binary`]. + pub semaphore_type: SemaphoreType, + + /// If `semaphore_type` is [`SemaphoreType::Timeline`], + /// specifies the counter value that the semaphore has when it is created. + /// + /// If `semaphore_type` is [`SemaphoreType::Binary`], then this must be `0`. + /// + /// The default value is `0`. + pub initial_value: u64, + /// The handle types that can be exported from the semaphore. /// /// The default value is [`ExternalSemaphoreHandleTypes::empty()`]. @@ -748,6 +1193,8 @@ impl Default for SemaphoreCreateInfo { #[inline] fn default() -> Self { Self { + semaphore_type: SemaphoreType::Binary, + initial_value: 0, export_handle_types: ExternalSemaphoreHandleTypes::empty(), _ne: crate::NonExhaustive(()), } @@ -757,10 +1204,43 @@ impl Default for SemaphoreCreateInfo { impl SemaphoreCreateInfo { pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { let &Self { + semaphore_type, + initial_value, export_handle_types, _ne: _, } = self; + semaphore_type.validate_device(device).map_err(|err| { + err.add_context("semaphore_type") + .set_vuids(&["VUID-VkSemaphoreTypeCreateInfo-semaphoreType-parameter"]) + })?; + + match semaphore_type { + SemaphoreType::Binary => { + if initial_value != 0 { + return Err(Box::new(ValidationError { + problem: "`semaphore_type` is `SemaphoreType::Binary`, but \ + `initial_value` is not `0`" + .into(), + vuids: &["VUID-VkSemaphoreTypeCreateInfo-semaphoreType-03279"], + ..Default::default() + })); + } + } + SemaphoreType::Timeline => { + if !device.enabled_features().timeline_semaphore { + return Err(Box::new(ValidationError { + context: "semaphore_type".into(), + problem: "is `SemaphoreType::Timeline`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "timeline_semaphore", + )])]), + vuids: &["VUID-VkSemaphoreTypeCreateInfo-timelineSemaphore-03252"], + })); + } + } + } + if !export_handle_types.is_empty() { if !(device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_external_semaphore) @@ -785,9 +1265,11 @@ impl SemaphoreCreateInfo { let external_semaphore_properties = unsafe { device .physical_device() - .external_semaphore_properties_unchecked( - ExternalSemaphoreInfo::handle_type(handle_type), - ) + .external_semaphore_properties_unchecked(ExternalSemaphoreInfo { + semaphore_type, + initial_value, + ..ExternalSemaphoreInfo::handle_type(handle_type) + }) }; if !external_semaphore_properties.exportable { @@ -829,6 +1311,24 @@ impl SemaphoreCreateInfo { } } +vulkan_enum! { + #[non_exhaustive] + + /// The type that a semaphore can have. + SemaphoreType = SemaphoreType(i32); + + /// A semaphore that can only have two states: unsignaled and signaled. + /// At any given time, only one pending operation may signal a binary semaphore, and only + /// one pending operation may wait on it. + Binary = BINARY, + + /// A semaphore whose state is a monotonically increasing integer. Signaling and waiting + /// operations have an associated semaphore value: signaling a timeline semaphore sets it to + /// the associated value, while waiting for a timeline semaphore will wait + /// until the current semaphore state is greater than or equal to the associated value. + Timeline = TIMELINE, +} + vulkan_bitflags_enum! { #[non_exhaustive] @@ -893,6 +1393,198 @@ vulkan_bitflags_enum! { ]), } +/// Parameters to signal a timeline semaphore. +#[derive(Clone, Debug)] +pub struct SemaphoreSignalInfo { + /// The new value to set the semaphore's counter to. + /// + /// The default value is `0`. + pub value: u64, + + pub _ne: crate::NonExhaustive, +} + +impl Default for SemaphoreSignalInfo { + #[inline] + fn default() -> Self { + Self { + value: 0, + _ne: crate::NonExhaustive(()), + } + } +} + +impl SemaphoreSignalInfo { + pub(crate) fn validate(&self, _device: &Device) -> Result<(), Box> { + let &Self { value: _, _ne: _ } = self; + + // unsafe + // VUID-VkSemaphoreSignalInfo-value-03258 + // VUID-VkSemaphoreSignalInfo-value-03259 + // VUID-VkSemaphoreSignalInfo-value-03260 + + Ok(()) + } +} + +/// Parameters to wait for a single timeline semaphore. +#[derive(Clone, Debug)] +pub struct SemaphoreWaitInfo { + /// Additional properties of the wait operation. + /// + /// The default value is empty. + pub flags: SemaphoreWaitFlags, + + /// The value to wait for. + /// + /// The default value is `0`. + pub value: u64, + + pub _ne: crate::NonExhaustive, +} + +impl Default for SemaphoreWaitInfo { + #[inline] + fn default() -> Self { + Self { + flags: SemaphoreWaitFlags::empty(), + value: 0, + _ne: crate::NonExhaustive(()), + } + } +} + +impl SemaphoreWaitInfo { + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + flags, + value: _, + _ne: _, + } = self; + + flags.validate_device(device).map_err(|err| { + err.add_context("flags") + .set_vuids(&["VUID-VkSemaphoreWaitInfo-flags-parameter"]) + })?; + + Ok(()) + } +} + +/// Parameters to wait for multiple timeline semaphores. +#[derive(Clone, Debug)] +pub struct SemaphoreWaitMultipleInfo { + /// Additional properties of the wait operation. + /// + /// The default value is empty. + pub flags: SemaphoreWaitFlags, + + /// The semaphores to wait for, and the values to wait for. + /// + /// The default value is empty. + pub semaphores: Vec, + + pub _ne: crate::NonExhaustive, +} + +impl Default for SemaphoreWaitMultipleInfo { + #[inline] + fn default() -> Self { + Self { + flags: SemaphoreWaitFlags::empty(), + semaphores: Vec::new(), + _ne: crate::NonExhaustive(()), + } + } +} + +impl SemaphoreWaitMultipleInfo { + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + flags, + ref semaphores, + _ne, + } = self; + + flags.validate_device(device).map_err(|err| { + err.add_context("flags") + .set_vuids(&["VUID-VkSemaphoreWaitInfo-flags-parameter"]) + })?; + + if semaphores.is_empty() { + return Ok(()); + } + + for (index, value_info) in semaphores.iter().enumerate() { + value_info + .validate(device) + .map_err(|err| err.add_context(format!("semaphores[{}]", index)))?; + } + + Ok(()) + } +} + +vulkan_bitflags! { + #[non_exhaustive] + + /// Flags specifying additional properties of a semaphore wait operation. + SemaphoreWaitFlags = SemaphoreWaitFlags(u32); + + /// Wait for at least one of the semaphores to signal the required value, + /// rather than all of them. + ANY = ANY, +} + +/// A semaphore to wait for, along with the value to wait for. +#[derive(Clone, Debug)] +pub struct SemaphoreWaitValueInfo { + /// The semaphore to wait for. + /// + /// There is no default value. + pub semaphore: Arc, + + /// The value to wait for. + /// + /// There is no default value. + pub value: u64, + + pub _ne: crate::NonExhaustive, +} + +impl SemaphoreWaitValueInfo { + /// Returns a `SemaphoreWaitValueInfo` with the specified `semaphore` and `value`. + #[inline] + pub fn new(semaphore: Arc, value: u64) -> Self { + Self { + semaphore, + value, + _ne: crate::NonExhaustive(()), + } + } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref semaphore, + value: _, + _ne: _, + } = self; + + assert_eq!(device, semaphore.device.as_ref()); + + if semaphore.semaphore_type != SemaphoreType::Timeline { + return Err(Box::new(ValidationError { + context: "semaphore.semaphore_type()".into(), + problem: "is not `SemaphoreType::Timeline`".into(), + vuids: &["VUID-VkSemaphoreWaitInfo-pSemaphores-03256"], + ..Default::default() + })); + } + + Ok(()) + } +} + vulkan_bitflags! { #[non_exhaustive] @@ -1170,6 +1862,16 @@ pub struct ExternalSemaphoreInfo { /// There is no default value. pub handle_type: ExternalSemaphoreHandleType, + /// The type that the semaphore will have. + /// + /// The default value is [`SemaphoreType::Binary`]. + pub semaphore_type: SemaphoreType, + + /// The initial value that the semaphore will have. + /// + /// The default value is `0`. + pub initial_value: u64, + pub _ne: crate::NonExhaustive, } @@ -1179,6 +1881,8 @@ impl ExternalSemaphoreInfo { pub fn handle_type(handle_type: ExternalSemaphoreHandleType) -> Self { Self { handle_type, + semaphore_type: SemaphoreType::Binary, + initial_value: 0, _ne: crate::NonExhaustive(()), } } @@ -1189,6 +1893,8 @@ impl ExternalSemaphoreInfo { ) -> Result<(), Box> { let &Self { handle_type, + semaphore_type, + initial_value, _ne: _, } = self; @@ -1199,6 +1905,39 @@ impl ExternalSemaphoreInfo { .set_vuids(&["VUID-VkPhysicalDeviceExternalSemaphoreInfo-handleType-parameter"]) })?; + semaphore_type + .validate_physical_device(physical_device) + .map_err(|err| { + err.add_context("semaphore_type") + .set_vuids(&["VUID-VkSemaphoreTypeCreateInfo-semaphoreType-parameter"]) + })?; + + match semaphore_type { + SemaphoreType::Binary => { + if initial_value != 0 { + return Err(Box::new(ValidationError { + problem: "`semaphore_type` is `SemaphoreType::Binary`, but \ + `initial_value` is not `0`" + .into(), + vuids: &["VUID-VkSemaphoreTypeCreateInfo-semaphoreType-03279"], + ..Default::default() + })); + } + } + SemaphoreType::Timeline => { + if !physical_device.supported_features().timeline_semaphore { + return Err(Box::new(ValidationError { + context: "semaphore_type".into(), + problem: "is `SemaphoreType::Timeline`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "timeline_semaphore", + )])]), + vuids: &["VUID-VkSemaphoreTypeCreateInfo-timelineSemaphore-03252"], + })); + } + } + } + Ok(()) } }