Skip to main content

enki/enki_api/resources/vec/
state.rs

1use super::core::GpuVec;
2use crate::enki_api::context::active_engine;
3use crate::enki_api::context::ambient::{is_inside_active_flow, resolve_user_caller};
4use crate::enki_api::context::errors::emit_and_abort;
5
6/// The temporal execution phase of a GPU buffer relative to the command timeline.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum BufferPhase {
9    /// Buffer is idle and safe for immediate host manipulation.
10    Idle,
11    /// Buffer is currently involved in recording commands within an active flow.
12    Recording,
13    /// Buffer has commands submitted and executing on the GPU hardware.
14    InFlight { target_timeline: u64 },
15}
16
17#[derive(Debug)]
18pub struct PhaseViolationError {
19    pub root_id: usize,
20    pub phase: BufferPhase,
21    pub operation: &'static str,
22}
23
24impl std::fmt::Display for PhaseViolationError {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        write!(
27            f,
28            "illegal host {op} on GpuVec (root id: {root}) during GPU command recording phase",
29            op = self.operation,
30            root = self.root_id,
31        )
32    }
33}
34
35impl std::error::Error for PhaseViolationError {}
36
37impl<T: Copy + Send + Sync + 'static> GpuVec<T> {
38    /// Queries the real-time execution phase of this buffer.
39    pub fn current_phase(&self) -> BufferPhase {
40        if is_inside_active_flow() {
41            return BufferPhase::Recording;
42        }
43
44        let engine = active_engine();
45        let target_timeline = engine
46            .timeline_counter
47            .load(std::sync::atomic::Ordering::SeqCst);
48        let current_gpu_timeline = engine.timeline_semaphore.get_timeline_value().unwrap_or(0);
49
50        if current_gpu_timeline < target_timeline {
51            BufferPhase::InFlight { target_timeline }
52        } else {
53            BufferPhase::Idle
54        }
55    }
56
57    #[track_caller]
58    #[inline(always)]
59    pub(crate) fn assert_host_readable(&self, operation: &'static str) {
60        if let BufferPhase::Recording = self.current_phase() {
61            let raw_caller = std::panic::Location::caller();
62            let caller = resolve_user_caller(raw_caller);
63            let diag = anu::diagnostics::rt::phase_violation(operation, caller);
64            emit_and_abort(&diag);
65        }
66    }
67
68    /// Returns `true` if the buffer is idle and safe for host access without waiting.
69    #[inline(always)]
70    pub fn is_idle(&self) -> bool {
71        matches!(self.current_phase(), BufferPhase::Idle)
72    }
73
74    /// Returns `true` if the buffer is currently involved in an active flow recording.
75    #[inline(always)]
76    pub fn is_recording(&self) -> bool {
77        matches!(self.current_phase(), BufferPhase::Recording)
78    }
79}