Skip to main content

vyre_driver/backend/
artifact_lifecycle.rs

1use std::collections::BTreeMap;
2
3use vyre_megakernel::{
4    Artifact, ArtifactValueId, Digest, TargetPayload, TargetPayloadFormat, TargetProfile,
5};
6
7use super::BackendError;
8
9/// Immutable identity of one acquired execution device generation.
10#[derive(Clone, Debug, PartialEq, Eq, Hash)]
11pub struct DeviceIdentity {
12    /// Stable registered backend identifier.
13    pub backend: &'static str,
14    /// Backend-local physical or logical device identifier.
15    pub device: String,
16    /// Monotonic generation that changes after device loss or reacquisition.
17    pub generation: u64,
18}
19
20/// Acquired device identity, target compatibility, and health.
21pub trait Device: Send + Sync {
22    /// Immutable identity for this acquired generation.
23    fn identity(&self) -> &DeviceIdentity;
24    /// Exact target payload representation admitted by this device.
25    fn target_format(&self) -> &TargetPayloadFormat;
26    /// Exact immutable compilation profile admitted by this device.
27    fn target_profile(&self) -> &TargetProfile;
28    /// Whether new materialization and submission are currently allowed.
29    fn is_healthy(&self) -> bool;
30}
31
32/// Host or resident bytes bound to one canonical artifact value.
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub enum BoundResource {
35    /// Caller-owned bytes uploaded for this submission.
36    Host(Vec<u8>),
37    /// Backend-resident resource handle.
38    Resident(super::Resource),
39}
40
41/// Complete typed bindings for one immutable artifact instance.
42#[derive(Clone, Debug, PartialEq, Eq)]
43pub struct BindingSet {
44    artifact: Digest,
45    resources: BTreeMap<ArtifactValueId, BoundResource>,
46    invocation_grid: Option<[u32; 3]>,
47}
48
49impl BindingSet {
50    /// Construct an empty binding set tied to one artifact identity.
51    #[must_use]
52    pub const fn new(artifact: Digest) -> Self {
53        Self {
54            artifact,
55            resources: BTreeMap::new(),
56            invocation_grid: None,
57        }
58    }
59
60    /// Artifact identity these bindings are valid for.
61    #[must_use]
62    pub const fn artifact(&self) -> Digest {
63        self.artifact
64    }
65
66    /// Bind or replace one canonical value.
67    pub fn insert(&mut self, value: ArtifactValueId, resource: BoundResource) {
68        self.resources.insert(value, resource);
69    }
70
71    /// Exact canonical value bindings.
72    #[must_use]
73    pub const fn resources(&self) -> &BTreeMap<ArtifactValueId, BoundResource> {
74        &self.resources
75    }
76
77    /// Set the runtime invocation grid without changing immutable artifact identity.
78    pub fn set_invocation_grid(&mut self, grid: [u32; 3]) -> Result<(), BackendError> {
79        if let Some(axis) = grid.iter().position(|extent| *extent == 0) {
80            return Err(BackendError::InvalidProgram {
81                fix: format!(
82                    "Fix: invocation grid axis {axis} must be positive, got {}.",
83                    grid[axis]
84                ),
85            });
86        }
87        self.invocation_grid = Some(grid);
88        Ok(())
89    }
90
91    /// Runtime grid override for this invocation.
92    #[must_use]
93    pub const fn invocation_grid(&self) -> Option<[u32; 3]> {
94        self.invocation_grid
95    }
96}
97
98/// Completed typed submission result.
99#[derive(Clone, Debug, PartialEq, Eq)]
100pub struct Completion {
101    /// Artifact identity executed by this submission.
102    pub artifact: Digest,
103    /// Canonical output values keyed by artifact ABI identity.
104    pub outputs: BTreeMap<ArtifactValueId, Vec<u8>>,
105    /// Updated retained values keyed by artifact ABI identity.
106    pub retained: BTreeMap<ArtifactValueId, Vec<u8>>,
107    /// Backend-measured device duration when available.
108    pub device_ns: Option<u64>,
109}
110
111/// One in-flight submission against an immutable artifact instance.
112pub trait Submission: Send + Sync {
113    /// Non-blocking completion probe.
114    fn is_ready(&self) -> bool;
115    /// Wait for completion and typed readback.
116    fn wait(self: Box<Self>) -> Result<Completion, BackendError>;
117}
118
119/// Device-native immutable executable and resource layout.
120pub trait ArtifactInstance: Send + Sync {
121    /// Neutral artifact identity implemented by this instance.
122    fn artifact(&self) -> Digest;
123    /// Exact payload identity materialized into this instance.
124    fn payload(&self) -> Digest;
125    /// Device generation that owns every native handle.
126    fn device(&self) -> &DeviceIdentity;
127    /// Validate bindings and submit one invocation.
128    fn submit(&self, bindings: BindingSet) -> Result<Box<dyn Submission>, BackendError>;
129}
130
131/// Device-specific admission and native-handle construction.
132pub trait ArtifactMaterializer: Send + Sync {
133    /// Acquired target device.
134    fn device(&self) -> &dyn Device;
135
136    /// Allocate one resource owned by this materializer's device generation.
137    fn allocate_resident(&self, _byte_len: usize) -> Result<super::Resource, BackendError> {
138        Err(BackendError::UnsupportedFeature {
139            name: "artifact resident buffer allocation".to_string(),
140            backend: self.device().identity().backend.to_string(),
141        })
142    }
143
144    /// Upload bytes into one resource owned by this materializer.
145    fn upload_resident(
146        &self,
147        _resource: &super::Resource,
148        _bytes: &[u8],
149    ) -> Result<(), BackendError> {
150        Err(BackendError::UnsupportedFeature {
151            name: "artifact resident buffer upload".to_string(),
152            backend: self.device().identity().backend.to_string(),
153        })
154    }
155
156    /// Upload bytes at one aligned offset into a resident resource.
157    fn upload_resident_at(
158        &self,
159        resource: &super::Resource,
160        offset_bytes: usize,
161        bytes: &[u8],
162    ) -> Result<(), BackendError> {
163        if offset_bytes == 0 {
164            return self.upload_resident(resource, bytes);
165        }
166        Err(BackendError::UnsupportedFeature {
167            name: "artifact resident ranged upload".to_string(),
168            backend: self.device().identity().backend.to_string(),
169        })
170    }
171
172    /// Release one resource owned by this materializer.
173    fn free_resident(&self, _resource: super::Resource) -> Result<(), BackendError> {
174        Err(BackendError::UnsupportedFeature {
175            name: "artifact resident buffer free".to_string(),
176            backend: self.device().identity().backend.to_string(),
177        })
178    }
179
180    /// Materialize authenticated immutable target bytes.
181    fn materialize(
182        &self,
183        artifact: &Artifact,
184        payload: &TargetPayload,
185    ) -> Result<Box<dyn ArtifactInstance>, BackendError>;
186}