atagen

monolith

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