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    /// Remove all entries matching `semaphore` from the list. Returns `true`
151    /// if anything was removed.
152    pub fn remove(&mut self, semaphore: vk::Semaphore) -> bool {
153        let mut removed = false;
154        let mut i = 0;
155        while i < self.semaphores.len() {
156            if self.semaphores[i] == semaphore {
157                self.semaphores.swap_remove(i);
158                if !self.values.is_empty() {
159                    self.values.swap_remove(i);
160                }
161                if !self.stage_masks.is_empty() {
162                    self.stage_masks.swap_remove(i);
163                }
164                removed = true;
165            } else {
166                i += 1;
167            }
168        }
169        self.check();
170        removed
171    }
172
173    /// Append `other` to `self`, leaving `other` empty.
174    pub fn append(&mut self, other: &mut Self) {
175        assert_eq!(self.mode, other.mode);
176
177        // If we're about to receive values, ensure we're aligned first.
178        if !other.values.is_empty() {
179            self.pad_values();
180        }
181        self.semaphores.append(&mut other.semaphores);
182        self.values.append(&mut other.values);
183        // If we had values, but `other` did not, re-align.
184        if !self.values.is_empty() {
185            self.pad_values();
186        }
187        self.stage_masks.append(&mut other.stage_masks);
188        self.check();
189    }
190
191    /// Pad `self.values` with dummy values for binary semaphores,
192    /// in preparation for adding a timeline semaphore value.
193    ///
194    /// This is a no-op if we already have values.
195    fn pad_values(&mut self) {
196        self.values.resize(self.semaphores.len(), !0);
197    }
198
199    #[track_caller]
200    fn check(&self) {
201        debug_assert!(self.values.is_empty() || self.values.len() == self.semaphores.len());
202        match self.mode {
203            SemaphoreListMode::Wait => {
204                debug_assert!(
205                    self.stage_masks.is_empty() || self.stage_masks.len() == self.semaphores.len()
206                );
207            }
208            SemaphoreListMode::Signal => {
209                debug_assert!(self.stage_masks.is_empty());
210            }
211        }
212    }
213}
214
215pub enum SemaphoreType {
216    Binary(vk::Semaphore),
217    Timeline(vk::Semaphore, u64),
218}