enki/enki_api/resources/vec/
state.rs1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum BufferPhase {
9 Idle,
11 Recording,
13 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 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 #[inline(always)]
70 pub fn is_idle(&self) -> bool {
71 matches!(self.current_phase(), BufferPhase::Idle)
72 }
73
74 #[inline(always)]
76 pub fn is_recording(&self) -> bool {
77 matches!(self.current_phase(), BufferPhase::Recording)
78 }
79}