wgpu_hal/vulkan/
semaphore_list.rs

1//! Definition of the [`SemaphoreList`] type.
2
3use alloc::vec::Vec;
4use ash::vk;
5use core::mem::MaybeUninit;
6
7#[derive(Debug, PartialEq)]
8pub enum SemaphoreListMode {
9    Wait,
10    Signal,
11}
12
13/// A list of Vulkan semaphores to wait for or signal.
14///
15/// This represents a list of binary or timeline semaphores, together
16/// with values for the timeline semaphores, and stage masks, if these
17/// are used for waiting.
18///
19/// This type ensures that the array of semaphores to be signaled
20/// stays aligned with the array of values for timeline semaphores
21/// appearing in that list. The [`add_to_submit`] method prepares the
22/// `vkQueueSubmit` arguments appropriately for whatever semaphores we
23/// actually have.
24///
25/// [`add_to_submit`]: SemaphoreList::add_to_submit
26#[derive(Debug)]
27pub struct SemaphoreList {
28    /// Mode of the semaphore list. Used for validation.
29    mode: SemaphoreListMode,
30
31    /// Semaphores to use.
32    ///
33    /// This can be a mix of binary and timeline semaphores.
34    semaphores: Vec<vk::Semaphore>,
35
36    /// Values for the timeline semaphores.
37    ///
38    /// If no timeline semaphores are present in [`semaphores`], this
39    /// is empty. If any timeline semaphores are present, then this
40    /// has the same length as [`semaphores`], with dummy !0 values
41    /// in the elements corresponding to binary semaphores, since
42    /// Vulkan ignores these.
43    ///
44    /// [`semaphores`]: Self::semaphores
45    values: Vec<u64>,
46
47    /// Stage masks for wait semaphores.
48    ///
49    /// This is only used if `mode` is `Wait`.
50    pub stage_masks: Vec<vk::PipelineStageFlags>,
51}
52
53impl SemaphoreList {
54    pub fn new(mode: SemaphoreListMode) -> Self {
55        Self {
56            mode,
57            semaphores: Vec::new(),
58            values: Vec::new(),
59            stage_masks: Vec::new(),
60        }
61    }
62
63    pub fn is_empty(&self) -> bool {
64        self.semaphores.is_empty()
65    }
66
67    /// Add this list to the semaphores to be signalled by a `vkQueueSubmit` call.
68    ///
69    /// - Set `submit_info`'s `pSignalSemaphores` list to this list's
70    ///   semaphores.
71    ///
72    /// - If this list contains any timeline semaphores, then initialize
73    ///   `timeline_info`, set its `pSignalSemaphoreValues` to this
74    ///   list's values, and add it to `submit_info`s extension chain.
75    ///
76    /// Return the revised `submit_info` value.
77    pub fn add_to_submit<'info, 'semaphores: 'info>(
78        wait_semaphores: &'semaphores mut Self,
79        signal_semaphores: &'semaphores mut Self,
80        submit_info: vk::SubmitInfo<'info>,
81        timeline_info: &'info mut MaybeUninit<vk::TimelineSemaphoreSubmitInfo<'info>>,
82    ) -> vk::SubmitInfo<'info> {
83        wait_semaphores.check();
84        signal_semaphores.check();
85
86        assert!(matches!(wait_semaphores.mode, SemaphoreListMode::Wait));
87        assert!(matches!(signal_semaphores.mode, SemaphoreListMode::Signal));
88
89        let timeline_info = timeline_info.write(vk::TimelineSemaphoreSubmitInfo::default());
90
91        let mut uses_timeline = false;
92
93        if !wait_semaphores.values.is_empty() {
94            *timeline_info = timeline_info.wait_semaphore_values(&wait_semaphores.values);
95            uses_timeline = true;
96        }
97
98        if !signal_semaphores.values.is_empty() {
99            *timeline_info = timeline_info.signal_semaphore_values(&signal_semaphores.values);
100            uses_timeline = true;
101        }
102
103        let mut submit_info = submit_info
104            .wait_semaphores(&wait_semaphores.semaphores)
105            .wait_dst_stage_mask(&wait_semaphores.stage_masks)
106            .signal_semaphores(&signal_semaphores.semaphores);
107
108        if uses_timeline {
109            submit_info = submit_info.push_next(timeline_info);
110        }
111
112        submit_info
113    }
114
115    /// Add a semaphore to be signaled. Panics if this is a list of semaphores to wait.
116    pub fn push_signal(&mut self, semaphore: SemaphoreType) {
117        assert!(matches!(self.mode, SemaphoreListMode::Signal));
118        self.push_inner(semaphore);
119    }
120
121    /// Add a semaphore to be waited for. Panics if this is a list of semaphores to signal.
122    pub fn push_wait(&mut self, semaphore: SemaphoreType, stage: vk::PipelineStageFlags) {
123        assert!(matches!(self.mode, SemaphoreListMode::Wait));
124
125        self.stage_masks.push(stage);
126        self.push_inner(semaphore);
127    }
128
129    fn push_inner(&mut self, semaphore: SemaphoreType) {
130        match semaphore {
131            SemaphoreType::Binary(semaphore) => {
132                self.semaphores.push(semaphore);
133                // Push a dummy value if necessary.
134                if !self.values.is_empty() {
135                    self.values.push(!0);
136                }
137            }
138            SemaphoreType::Timeline(semaphore, value) => {
139                // We may be the first timeline semaphore, ensure that the values
140                // array is filled with dummy values for existing binary semaphores.
141                self.pad_values();
142                self.semaphores.push(semaphore);
143                self.values.push(value);
144            }
145        }
146
147        self.check();
148    }
149
150    /// Append `other` to `self`, leaving `other` empty.
151    pub fn append(&mut self, other: &mut Self) {
152        assert_eq!(self.mode, other.mode);
153
154        // If we're about to receive values, ensure we're aligned first.
155        if !other.values.is_empty() {
156            self.pad_values();
157        }
158        self.semaphores.append(&mut other.semaphores);
159        self.values.append(&mut other.values);
160        // If we had values, but `other` did not, re-align.
161        if !self.values.is_empty() {
162            self.pad_values();
163        }
164        self.stage_masks.append(&mut other.stage_masks);
165        self.check();
166    }
167
168    /// Pad `self.values` with dummy values for binary semaphores,
169    /// in preparation for adding a timeline semaphore value.
170    ///
171    /// This is a no-op if we already have values.
172    fn pad_values(&mut self) {
173        self.values.resize(self.semaphores.len(), !0);
174    }
175
176    #[track_caller]
177    fn check(&self) {
178        debug_assert!(self.values.is_empty() || self.values.len() == self.semaphores.len());
179        match self.mode {
180            SemaphoreListMode::Wait => {
181                debug_assert!(
182                    self.stage_masks.is_empty() || self.stage_masks.len() == self.semaphores.len()
183                );
184            }
185            SemaphoreListMode::Signal => {
186                debug_assert!(self.stage_masks.is_empty());
187            }
188        }
189    }
190}
191
192pub enum SemaphoreType {
193    Binary(vk::Semaphore),
194    Timeline(vk::Semaphore, u64),
195}