atagen

monolith

public
Star0
Watch0
f8499fd614ae7e891ae36b7456b5c3f20cf3f39d
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git

Blame: crates/render-vk/src/sync.rs

824bf02b6e55atagen2026-10-16 09:15 UTC1
use crate::device::Shared;
6dfb54e27e7fatagen2026-10-19 07:53 UTC2
use crate::error::{WAIT, observe, waited};
824bf02b6e55atagen2026-10-16 09:15 UTC3
use ash::vk;
7de49c2aa70batagen2026-10-16 09:27 UTC4
use rustix::event::{PollFd, PollFlags, poll};
824bf02b6e55atagen2026-10-16 09:15 UTC5
use smithay::backend::renderer::sync::{Fence, Interrupted};
824bf02b6e55atagen2026-10-16 09:15 UTC6
use std::fmt;
7de49c2aa70batagen2026-10-16 09:27 UTC7
use std::os::fd::{FromRawFd, OwnedFd};
7de49c2aa70batagen2026-10-16 09:27 UTC8
use std::sync::{Arc, Mutex};
7de49c2aa70batagen2026-10-16 09:27 UTC9
7de49c2aa70batagen2026-10-16 09:27 UTC10
enum State {
7de49c2aa70batagen2026-10-16 09:27 UTC11
    Pending,
7de49c2aa70batagen2026-10-16 09:27 UTC12
    /// Exporting a sync_file resets the Vulkan fence, so from then on the
7de49c2aa70batagen2026-10-16 09:27 UTC13
    /// file is the only record of completion. `None` means it had already
7de49c2aa70batagen2026-10-16 09:27 UTC14
    /// signalled when exported.
7de49c2aa70batagen2026-10-16 09:27 UTC15
    Exported(Option<OwnedFd>),
7de49c2aa70batagen2026-10-16 09:27 UTC16
}
824bf02b6e55atagen2026-10-16 09:15 UTC17
824bf02b6e55atagen2026-10-16 09:15 UTC18
pub(crate) struct FenceInner {
7de49c2aa70batagen2026-10-16 09:27 UTC19
    shared: Arc<Shared>,
7de49c2aa70batagen2026-10-16 09:27 UTC20
    fence: vk::Fence,
7de49c2aa70batagen2026-10-16 09:27 UTC21
    exportable: bool,
7de49c2aa70batagen2026-10-16 09:27 UTC22
    state: Mutex<State>,
824bf02b6e55atagen2026-10-16 09:15 UTC23
}
824bf02b6e55atagen2026-10-16 09:15 UTC24
824bf02b6e55atagen2026-10-16 09:15 UTC25
impl FenceInner {
824bf02b6e55atagen2026-10-16 09:15 UTC26
    pub fn new(shared: &Arc<Shared>) -> Result<Arc<Self>, vk::Result> {
7de49c2aa70batagen2026-10-16 09:27 UTC27
        let exportable = shared.device.dmabuf.is_some();
7de49c2aa70batagen2026-10-16 09:27 UTC28
        let mut export = vk::ExportFenceCreateInfo::default()
7de49c2aa70batagen2026-10-16 09:27 UTC29
            .handle_types(vk::ExternalFenceHandleTypeFlags::SYNC_FD);
7de49c2aa70batagen2026-10-16 09:27 UTC30
        let mut info = vk::FenceCreateInfo::default();
7de49c2aa70batagen2026-10-16 09:27 UTC31
        if exportable {
7de49c2aa70batagen2026-10-16 09:27 UTC32
            info = info.push_next(&mut export);
7de49c2aa70batagen2026-10-16 09:27 UTC33
        }
d5eca1c82213atagen2026-10-16 13:56 UTC34
        let spare = shared
d5eca1c82213atagen2026-10-16 13:56 UTC35
            .device
d5eca1c82213atagen2026-10-16 13:56 UTC36
            .spare_fences
d5eca1c82213atagen2026-10-16 13:56 UTC37
            .lock()
d5eca1c82213atagen2026-10-16 13:56 UTC38
            .unwrap_or_else(|e| e.into_inner())
d5eca1c82213atagen2026-10-16 13:56 UTC39
            .pop();
d5eca1c82213atagen2026-10-16 13:56 UTC40
        let fence = match spare {
d5eca1c82213atagen2026-10-16 13:56 UTC41
            Some(f) => {
d5eca1c82213atagen2026-10-16 13:56 UTC42
                unsafe { shared.device.device.reset_fences(&[f])? };
d5eca1c82213atagen2026-10-16 13:56 UTC43
                f
d5eca1c82213atagen2026-10-16 13:56 UTC44
            }
d5eca1c82213atagen2026-10-16 13:56 UTC45
            None => unsafe { shared.device.device.create_fence(&info, None)? },
d5eca1c82213atagen2026-10-16 13:56 UTC46
        };
824bf02b6e55atagen2026-10-16 09:15 UTC47
        Ok(Arc::new(Self {
824bf02b6e55atagen2026-10-16 09:15 UTC48
            shared: shared.clone(),
824bf02b6e55atagen2026-10-16 09:15 UTC49
            fence,
7de49c2aa70batagen2026-10-16 09:27 UTC50
            exportable,
7de49c2aa70batagen2026-10-16 09:27 UTC51
            state: Mutex::new(State::Pending),
824bf02b6e55atagen2026-10-16 09:15 UTC52
        }))
824bf02b6e55atagen2026-10-16 09:15 UTC53
    }
824bf02b6e55atagen2026-10-16 09:15 UTC54
7de49c2aa70batagen2026-10-16 09:27 UTC55
    pub fn fence(&self) -> vk::Fence {
7de49c2aa70batagen2026-10-16 09:27 UTC56
        self.fence
7de49c2aa70batagen2026-10-16 09:27 UTC57
    }
7de49c2aa70batagen2026-10-16 09:27 UTC58
6dfb54e27e7fatagen2026-10-19 07:53 UTC59
    /// Whether the GPU has signalled, or the error the query failed with.
6dfb54e27e7fatagen2026-10-19 07:53 UTC60
    pub fn status(&self) -> Result<bool, vk::Result> {
7de49c2aa70batagen2026-10-16 09:27 UTC61
        match &*self.state.lock().unwrap_or_else(|e| e.into_inner()) {
6dfb54e27e7fatagen2026-10-19 07:53 UTC62
            State::Pending => unsafe { self.shared.device.device.get_fence_status(self.fence) },
6dfb54e27e7fatagen2026-10-19 07:53 UTC63
            State::Exported(None) => Ok(true),
6dfb54e27e7fatagen2026-10-19 07:53 UTC64
            State::Exported(Some(fd)) => Ok(readable(fd, Some(&Default::default()))),
7de49c2aa70batagen2026-10-16 09:27 UTC65
        }
824bf02b6e55atagen2026-10-16 09:15 UTC66
    }
824bf02b6e55atagen2026-10-16 09:15 UTC67
6dfb54e27e7fatagen2026-10-19 07:53 UTC68
    /// Like `status` for callers that cannot report a failure. A failed query
6dfb54e27e7fatagen2026-10-19 07:53 UTC69
    /// counts as signalled so nobody waits on a fence that cannot signal.
6dfb54e27e7fatagen2026-10-19 07:53 UTC70
    pub fn signaled(&self) -> bool {
6dfb54e27e7fatagen2026-10-19 07:53 UTC71
        self.status().unwrap_or_else(|e| {
6dfb54e27e7fatagen2026-10-19 07:53 UTC72
            observe(e);
6dfb54e27e7fatagen2026-10-19 07:53 UTC73
            true
6dfb54e27e7fatagen2026-10-19 07:53 UTC74
        })
6dfb54e27e7fatagen2026-10-19 07:53 UTC75
    }
6dfb54e27e7fatagen2026-10-19 07:53 UTC76
cabbef6c998aatagen2026-10-16 13:42 UTC77
    /// Blocks until the GPU signals. A pending fence is first turned into its
cabbef6c998aatagen2026-10-16 13:42 UTC78
    /// sync_file and polled, because waiting on the Vulkan fence itself sleeps
cabbef6c998aatagen2026-10-16 13:42 UTC79
    /// in 10 ms steps on NVIDIA even when the work is done in microseconds.
6dfb54e27e7fatagen2026-10-19 07:53 UTC80
    /// Gives up with `TIMEOUT` after [`WAIT`], so a hung GPU cannot freeze the
6dfb54e27e7fatagen2026-10-19 07:53 UTC81
    /// caller.
824bf02b6e55atagen2026-10-16 09:15 UTC82
    pub fn wait(&self) -> Result<(), vk::Result> {
cabbef6c998aatagen2026-10-16 13:42 UTC83
        if self.exportable {
cabbef6c998aatagen2026-10-16 13:42 UTC84
            drop(self.export());
cabbef6c998aatagen2026-10-16 13:42 UTC85
        }
6dfb54e27e7fatagen2026-10-19 07:53 UTC86
        let waited_for = match &*self.state.lock().unwrap_or_else(|e| e.into_inner()) {
7de49c2aa70batagen2026-10-16 09:27 UTC87
            State::Pending => unsafe {
6dfb54e27e7fatagen2026-10-19 07:53 UTC88
                self.shared.device.device.wait_for_fences(
6dfb54e27e7fatagen2026-10-19 07:53 UTC89
                    &[self.fence],
6dfb54e27e7fatagen2026-10-19 07:53 UTC90
                    true,
6dfb54e27e7fatagen2026-10-19 07:53 UTC91
                    WAIT.as_nanos() as u64,
6dfb54e27e7fatagen2026-10-19 07:53 UTC92
                )
7de49c2aa70batagen2026-10-16 09:27 UTC93
            },
7de49c2aa70batagen2026-10-16 09:27 UTC94
            State::Exported(None) => Ok(()),
7de49c2aa70batagen2026-10-16 09:27 UTC95
            State::Exported(Some(fd)) => {
6dfb54e27e7fatagen2026-10-19 07:53 UTC96
                let timeout = rustix::event::Timespec {
6dfb54e27e7fatagen2026-10-19 07:53 UTC97
                    tv_sec: WAIT.as_secs() as _,
6dfb54e27e7fatagen2026-10-19 07:53 UTC98
                    tv_nsec: 0,
6dfb54e27e7fatagen2026-10-19 07:53 UTC99
                };
6dfb54e27e7fatagen2026-10-19 07:53 UTC100
                if readable(fd, Some(&timeout)) {
6dfb54e27e7fatagen2026-10-19 07:53 UTC101
                    Ok(())
6dfb54e27e7fatagen2026-10-19 07:53 UTC102
                } else {
6dfb54e27e7fatagen2026-10-19 07:53 UTC103
                    Err(vk::Result::TIMEOUT)
6dfb54e27e7fatagen2026-10-19 07:53 UTC104
                }
7de49c2aa70batagen2026-10-16 09:27 UTC105
            }
6dfb54e27e7fatagen2026-10-19 07:53 UTC106
        };
6dfb54e27e7fatagen2026-10-19 07:53 UTC107
        if waited_for.is_ok() {
6dfb54e27e7fatagen2026-10-19 07:53 UTC108
            waited();
7de49c2aa70batagen2026-10-16 09:27 UTC109
        }
6dfb54e27e7fatagen2026-10-19 07:53 UTC110
        waited_for
7de49c2aa70batagen2026-10-16 09:27 UTC111
    }
7de49c2aa70batagen2026-10-16 09:27 UTC112
cabbef6c998aatagen2026-10-16 13:42 UTC113
    #[profiling::function]
7de49c2aa70batagen2026-10-16 09:27 UTC114
    fn export(&self) -> Option<OwnedFd> {
7de49c2aa70batagen2026-10-16 09:27 UTC115
        if !self.exportable {
7de49c2aa70batagen2026-10-16 09:27 UTC116
            return None;
7de49c2aa70batagen2026-10-16 09:27 UTC117
        }
7de49c2aa70batagen2026-10-16 09:27 UTC118
        let ext = self.shared.device.dmabuf.as_ref()?;
7de49c2aa70batagen2026-10-16 09:27 UTC119
        let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
7de49c2aa70batagen2026-10-16 09:27 UTC120
        if let State::Pending = *state {
7de49c2aa70batagen2026-10-16 09:27 UTC121
            let info = vk::FenceGetFdInfoKHR::default()
7de49c2aa70batagen2026-10-16 09:27 UTC122
                .fence(self.fence)
7de49c2aa70batagen2026-10-16 09:27 UTC123
                .handle_type(vk::ExternalFenceHandleTypeFlags::SYNC_FD);
7de49c2aa70batagen2026-10-16 09:27 UTC124
            let raw = unsafe { ext.fence_fd.get_fence_fd(&info) }.ok()?;
7de49c2aa70batagen2026-10-16 09:27 UTC125
            *state = State::Exported((raw >= 0).then(|| unsafe { OwnedFd::from_raw_fd(raw) }));
7de49c2aa70batagen2026-10-16 09:27 UTC126
        }
7de49c2aa70batagen2026-10-16 09:27 UTC127
        match &*state {
7de49c2aa70batagen2026-10-16 09:27 UTC128
            State::Exported(Some(fd)) => fd.try_clone().ok(),
7de49c2aa70batagen2026-10-16 09:27 UTC129
            _ => None,
7de49c2aa70batagen2026-10-16 09:27 UTC130
        }
7de49c2aa70batagen2026-10-16 09:27 UTC131
    }
7de49c2aa70batagen2026-10-16 09:27 UTC132
}
7de49c2aa70batagen2026-10-16 09:27 UTC133
7de49c2aa70batagen2026-10-16 09:27 UTC134
fn readable(fd: &OwnedFd, timeout: Option<&rustix::event::Timespec>) -> bool {
7de49c2aa70batagen2026-10-16 09:27 UTC135
    let mut fds = [PollFd::new(fd, PollFlags::IN)];
7de49c2aa70batagen2026-10-16 09:27 UTC136
    loop {
7de49c2aa70batagen2026-10-16 09:27 UTC137
        match poll(&mut fds, timeout) {
7de49c2aa70batagen2026-10-16 09:27 UTC138
            Ok(n) => return n > 0,
7de49c2aa70batagen2026-10-16 09:27 UTC139
            Err(rustix::io::Errno::INTR) => continue,
7de49c2aa70batagen2026-10-16 09:27 UTC140
            Err(_) => return true,
824bf02b6e55atagen2026-10-16 09:15 UTC141
        }
824bf02b6e55atagen2026-10-16 09:15 UTC142
    }
824bf02b6e55atagen2026-10-16 09:15 UTC143
}
824bf02b6e55atagen2026-10-16 09:15 UTC144
d5eca1c82213atagen2026-10-16 13:56 UTC145
/// Fences go back to a pool instead of being destroyed, which cut the median
d5eca1c82213atagen2026-10-16 13:56 UTC146
/// queue submit on NVIDIA from 87 to 52 us. One still pending is destroyed.
824bf02b6e55atagen2026-10-16 09:15 UTC147
impl Drop for FenceInner {
824bf02b6e55atagen2026-10-16 09:15 UTC148
    fn drop(&mut self) {
d5eca1c82213atagen2026-10-16 13:56 UTC149
        let d = &self.shared.device.device;
d5eca1c82213atagen2026-10-16 13:56 UTC150
        let state = self.state.get_mut().unwrap_or_else(|e| e.into_inner());
d5eca1c82213atagen2026-10-16 13:56 UTC151
        let idle = match state {
d5eca1c82213atagen2026-10-16 13:56 UTC152
            State::Pending => unsafe { d.get_fence_status(self.fence) }.unwrap_or(false),
d5eca1c82213atagen2026-10-16 13:56 UTC153
            State::Exported(_) => true,
d5eca1c82213atagen2026-10-16 13:56 UTC154
        };
d5eca1c82213atagen2026-10-16 13:56 UTC155
        if idle {
d5eca1c82213atagen2026-10-16 13:56 UTC156
            self.shared
d5eca1c82213atagen2026-10-16 13:56 UTC157
                .device
d5eca1c82213atagen2026-10-16 13:56 UTC158
                .spare_fences
d5eca1c82213atagen2026-10-16 13:56 UTC159
                .lock()
d5eca1c82213atagen2026-10-16 13:56 UTC160
                .unwrap_or_else(|e| e.into_inner())
d5eca1c82213atagen2026-10-16 13:56 UTC161
                .push(self.fence);
d5eca1c82213atagen2026-10-16 13:56 UTC162
        } else {
d5eca1c82213atagen2026-10-16 13:56 UTC163
            unsafe { d.destroy_fence(self.fence, None) };
d5eca1c82213atagen2026-10-16 13:56 UTC164
        }
824bf02b6e55atagen2026-10-16 09:15 UTC165
    }
824bf02b6e55atagen2026-10-16 09:15 UTC166
}
824bf02b6e55atagen2026-10-16 09:15 UTC167
824bf02b6e55atagen2026-10-16 09:15 UTC168
impl fmt::Debug for FenceInner {
824bf02b6e55atagen2026-10-16 09:15 UTC169
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
824bf02b6e55atagen2026-10-16 09:15 UTC170
        f.debug_tuple("FenceInner").field(&self.fence).finish()
824bf02b6e55atagen2026-10-16 09:15 UTC171
    }
824bf02b6e55atagen2026-10-16 09:15 UTC172
}
824bf02b6e55atagen2026-10-16 09:15 UTC173
824bf02b6e55atagen2026-10-16 09:15 UTC174
#[derive(Debug)]
824bf02b6e55atagen2026-10-16 09:15 UTC175
pub(crate) struct VkFence(pub Arc<FenceInner>);
824bf02b6e55atagen2026-10-16 09:15 UTC176
824bf02b6e55atagen2026-10-16 09:15 UTC177
impl Fence for VkFence {
824bf02b6e55atagen2026-10-16 09:15 UTC178
    fn is_signaled(&self) -> bool {
824bf02b6e55atagen2026-10-16 09:15 UTC179
        self.0.signaled()
824bf02b6e55atagen2026-10-16 09:15 UTC180
    }
824bf02b6e55atagen2026-10-16 09:15 UTC181
824bf02b6e55atagen2026-10-16 09:15 UTC182
    fn wait(&self) -> Result<(), Interrupted> {
6dfb54e27e7fatagen2026-10-19 07:53 UTC183
        self.0.wait().map_err(|e| {
6dfb54e27e7fatagen2026-10-19 07:53 UTC184
            observe(e);
6dfb54e27e7fatagen2026-10-19 07:53 UTC185
            Interrupted
6dfb54e27e7fatagen2026-10-19 07:53 UTC186
        })
824bf02b6e55atagen2026-10-16 09:15 UTC187
    }
824bf02b6e55atagen2026-10-16 09:15 UTC188
824bf02b6e55atagen2026-10-16 09:15 UTC189
    fn is_exportable(&self) -> bool {
7de49c2aa70batagen2026-10-16 09:27 UTC190
        self.0.exportable
824bf02b6e55atagen2026-10-16 09:15 UTC191
    }
824bf02b6e55atagen2026-10-16 09:15 UTC192
7de49c2aa70batagen2026-10-16 09:27 UTC193
    fn export(&self) -> Option<OwnedFd> {
7de49c2aa70batagen2026-10-16 09:27 UTC194
        self.0.export()
824bf02b6e55atagen2026-10-16 09:15 UTC195
    }
824bf02b6e55atagen2026-10-16 09:15 UTC196
}