atagen

monolith

public
Star0
Watch0
d9aeb9e28e74d14bc8c35504f26fb3706ba3cf2a
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/render-vk/src/sync.rsRust196 lines6.1 KB
1
use crate::device::Shared;
2
use crate::error::{WAIT, observe, waited};
3
use ash::vk;
4
use rustix::event::{PollFd, PollFlags, poll};
5
use smithay::backend::renderer::sync::{Fence, Interrupted};
6
use std::fmt;
7
use std::os::fd::{FromRawFd, OwnedFd};
8
use std::sync::{Arc, Mutex};
9
10
enum State {
11
    Pending,
12
    /// Exporting a sync_file resets the Vulkan fence, so from then on the
13
    /// file is the only record of completion. `None` means it had already
14
    /// signalled when exported.
15
    Exported(Option<OwnedFd>),
16
}
17
18
pub(crate) struct FenceInner {
19
    shared: Arc<Shared>,
20
    fence: vk::Fence,
21
    exportable: bool,
22
    state: Mutex<State>,
23
}
24
25
impl FenceInner {
26
    pub fn new(shared: &Arc<Shared>) -> Result<Arc<Self>, vk::Result> {
27
        let exportable = shared.device.dmabuf.is_some();
28
        let mut export = vk::ExportFenceCreateInfo::default()
29
            .handle_types(vk::ExternalFenceHandleTypeFlags::SYNC_FD);
30
        let mut info = vk::FenceCreateInfo::default();
31
        if exportable {
32
            info = info.push_next(&mut export);
33
        }
34
        let spare = shared
35
            .device
36
            .spare_fences
37
            .lock()
38
            .unwrap_or_else(|e| e.into_inner())
39
            .pop();
40
        let fence = match spare {
41
            Some(f) => {
42
                unsafe { shared.device.device.reset_fences(&[f])? };
43
                f
44
            }
45
            None => unsafe { shared.device.device.create_fence(&info, None)? },
46
        };
47
        Ok(Arc::new(Self {
48
            shared: shared.clone(),
49
            fence,
50
            exportable,
51
            state: Mutex::new(State::Pending),
52
        }))
53
    }
54
55
    pub fn fence(&self) -> vk::Fence {
56
        self.fence
57
    }
58
59
    /// Whether the GPU has signalled, or the error the query failed with.
60
    pub fn status(&self) -> Result<bool, vk::Result> {
61
        match &*self.state.lock().unwrap_or_else(|e| e.into_inner()) {
62
            State::Pending => unsafe { self.shared.device.device.get_fence_status(self.fence) },
63
            State::Exported(None) => Ok(true),
64
            State::Exported(Some(fd)) => Ok(readable(fd, Some(&Default::default()))),
65
        }
66
    }
67
68
    /// Like `status` for callers that cannot report a failure. A failed query
69
    /// counts as signalled so nobody waits on a fence that cannot signal.
70
    pub fn signaled(&self) -> bool {
71
        self.status().unwrap_or_else(|e| {
72
            observe(e);
73
            true
74
        })
75
    }
76
77
    /// Blocks until the GPU signals. A pending fence is first turned into its
78
    /// sync_file and polled, because waiting on the Vulkan fence itself sleeps
79
    /// in 10 ms steps on NVIDIA even when the work is done in microseconds.
80
    /// Gives up with `TIMEOUT` after [`WAIT`], so a hung GPU cannot freeze the
81
    /// caller.
82
    pub fn wait(&self) -> Result<(), vk::Result> {
83
        if self.exportable {
84
            drop(self.export());
85
        }
86
        let waited_for = match &*self.state.lock().unwrap_or_else(|e| e.into_inner()) {
87
            State::Pending => unsafe {
88
                self.shared.device.device.wait_for_fences(
89
                    &[self.fence],
90
                    true,
91
                    WAIT.as_nanos() as u64,
92
                )
93
            },
94
            State::Exported(None) => Ok(()),
95
            State::Exported(Some(fd)) => {
96
                let timeout = rustix::event::Timespec {
97
                    tv_sec: WAIT.as_secs() as _,
98
                    tv_nsec: 0,
99
                };
100
                if readable(fd, Some(&timeout)) {
101
                    Ok(())
102
                } else {
103
                    Err(vk::Result::TIMEOUT)
104
                }
105
            }
106
        };
107
        if waited_for.is_ok() {
108
            waited();
109
        }
110
        waited_for
111
    }
112
113
    #[profiling::function]
114
    fn export(&self) -> Option<OwnedFd> {
115
        if !self.exportable {
116
            return None;
117
        }
118
        let ext = self.shared.device.dmabuf.as_ref()?;
119
        let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
120
        if let State::Pending = *state {
121
            let info = vk::FenceGetFdInfoKHR::default()
122
                .fence(self.fence)
123
                .handle_type(vk::ExternalFenceHandleTypeFlags::SYNC_FD);
124
            let raw = unsafe { ext.fence_fd.get_fence_fd(&info) }.ok()?;
125
            *state = State::Exported((raw >= 0).then(|| unsafe { OwnedFd::from_raw_fd(raw) }));
126
        }
127
        match &*state {
128
            State::Exported(Some(fd)) => fd.try_clone().ok(),
129
            _ => None,
130
        }
131
    }
132
}
133
134
fn readable(fd: &OwnedFd, timeout: Option<&rustix::event::Timespec>) -> bool {
135
    let mut fds = [PollFd::new(fd, PollFlags::IN)];
136
    loop {
137
        match poll(&mut fds, timeout) {
138
            Ok(n) => return n > 0,
139
            Err(rustix::io::Errno::INTR) => continue,
140
            Err(_) => return true,
141
        }
142
    }
143
}
144
145
/// Fences go back to a pool instead of being destroyed, which cut the median
146
/// queue submit on NVIDIA from 87 to 52 us. One still pending is destroyed.
147
impl Drop for FenceInner {
148
    fn drop(&mut self) {
149
        let d = &self.shared.device.device;
150
        let state = self.state.get_mut().unwrap_or_else(|e| e.into_inner());
151
        let idle = match state {
152
            State::Pending => unsafe { d.get_fence_status(self.fence) }.unwrap_or(false),
153
            State::Exported(_) => true,
154
        };
155
        if idle {
156
            self.shared
157
                .device
158
                .spare_fences
159
                .lock()
160
                .unwrap_or_else(|e| e.into_inner())
161
                .push(self.fence);
162
        } else {
163
            unsafe { d.destroy_fence(self.fence, None) };
164
        }
165
    }
166
}
167
168
impl fmt::Debug for FenceInner {
169
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170
        f.debug_tuple("FenceInner").field(&self.fence).finish()
171
    }
172
}
173
174
#[derive(Debug)]
175
pub(crate) struct VkFence(pub Arc<FenceInner>);
176
177
impl Fence for VkFence {
178
    fn is_signaled(&self) -> bool {
179
        self.0.signaled()
180
    }
181
182
    fn wait(&self) -> Result<(), Interrupted> {
183
        self.0.wait().map_err(|e| {
184
            observe(e);
185
            Interrupted
186
        })
187
    }
188
189
    fn is_exportable(&self) -> bool {
190
        self.0.exportable
191
    }
192
193
    fn export(&self) -> Option<OwnedFd> {
194
        self.0.export()
195
    }
196
}