Skip to main content

molgfx_gpu/residency/
upload_types.rs

1//! Public upload lifecycle values and telemetry.
2
3use thiserror::Error;
4
5/// Monotonic submission completion value supplied by a backend.
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
7pub struct FenceValue(pub u64);
8
9/// Stable identifier for one ring reservation.
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11pub struct UploadTicket(pub(super) u64);
12
13impl UploadTicket {
14    /// Monotonic ticket sequence, useful for tracing.
15    #[must_use]
16    pub const fn sequence(self) -> u64 {
17        self.0
18    }
19}
20
21/// Current lifecycle of an upload reservation.
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub enum UploadState {
24    /// Reserved bytes may be filled by the caller.
25    Reserved,
26    /// Staging bytes are complete and may be submitted.
27    Ready,
28    /// A backend submission may still read the bytes.
29    InFlight(FenceValue),
30    /// The reservation was cancelled before submission.
31    Cancelled,
32}
33
34/// Immutable description returned when bytes are reserved.
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub struct UploadReservation {
37    pub(super) ticket: UploadTicket,
38    pub(super) offset: usize,
39    pub(super) len: usize,
40}
41
42impl UploadReservation {
43    /// Ticket used for commit, submission and cancellation.
44    #[must_use]
45    pub const fn ticket(self) -> UploadTicket {
46        self.ticket
47    }
48
49    /// Offset into the ring's staging buffer.
50    #[must_use]
51    pub const fn offset(self) -> usize {
52        self.offset
53    }
54
55    /// Payload byte length.
56    #[must_use]
57    pub const fn len(self) -> usize {
58        self.len
59    }
60
61    /// Whether the payload is empty.
62    #[must_use]
63    pub const fn is_empty(self) -> bool {
64        self.len == 0
65    }
66}
67
68/// Fixed resource and upload throughput budgets.
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
70pub struct UploadRingConfig {
71    /// Staging bytes retained for the lifetime of the ring.
72    pub capacity_bytes: usize,
73    /// Maximum simultaneous reservations.
74    pub ticket_capacity: usize,
75    /// Maximum payload bytes accepted between calls to `begin_epoch`.
76    pub epoch_budget_bytes: usize,
77    /// Maximum submitted payload bytes awaiting fence completion.
78    pub in_flight_budget_bytes: usize,
79    /// Required payload offset alignment. Must be a power of two.
80    pub alignment: usize,
81}
82
83impl UploadRingConfig {
84    /// Validates capacities and budgets without allocating staging storage.
85    ///
86    /// # Errors
87    ///
88    /// Rejects empty capacities or budgets, an in-flight limit larger than
89    /// staging capacity, and alignment that is not a power of two.
90    pub const fn validate(self) -> Result<(), UploadBackpressure> {
91        if self.capacity_bytes == 0
92            || self.ticket_capacity == 0
93            || self.epoch_budget_bytes == 0
94            || self.in_flight_budget_bytes == 0
95            || self.in_flight_budget_bytes > self.capacity_bytes
96            || !self.alignment.is_power_of_two()
97        {
98            return Err(UploadBackpressure::InvalidConfiguration);
99        }
100        Ok(())
101    }
102}
103
104/// Observable ring counters. Values are cumulative except active gauges.
105#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
106pub struct UploadMetrics {
107    /// Payload bytes committed into staging.
108    pub bytes_staged: u64,
109    /// Payload bytes handed to backend submissions.
110    pub bytes_submitted: u64,
111    /// Payload bytes made reusable after fence completion.
112    pub bytes_retired: u64,
113    /// Payload bytes cancelled before submission.
114    pub bytes_cancelled: u64,
115    /// Payload bytes accepted in the current epoch.
116    pub epoch_bytes: u64,
117    /// Submitted payload bytes awaiting fence completion.
118    pub in_flight_bytes: u64,
119    /// Largest in-flight payload footprint observed.
120    pub peak_in_flight_bytes: u64,
121    /// Ring span occupied by live reservations, including padding.
122    pub occupied_bytes: u64,
123    /// Largest occupied ring span observed.
124    pub peak_occupied_bytes: u64,
125    /// Number of live reservations.
126    pub active_tickets: u64,
127    /// Requests rejected by any budget or capacity bound.
128    pub stall_events: u64,
129    /// Payload bytes represented by rejected requests.
130    pub stalled_bytes: u64,
131    /// Host storage allocations performed by this primitive.
132    pub host_allocation_events: u64,
133}
134
135/// Why an upload reservation or lifecycle transition could not proceed.
136#[derive(Clone, Copy, Debug, Error, PartialEq, Eq)]
137pub enum UploadBackpressure {
138    /// Configuration must provide non-zero capacities and power-of-two alignment.
139    #[error("upload ring configuration is invalid")]
140    InvalidConfiguration,
141    /// Empty uploads are rejected.
142    #[error("an upload reservation must contain at least one byte")]
143    EmptyUpload,
144    /// The configured per-epoch payload budget was exhausted.
145    #[error("upload epoch budget exhausted")]
146    EpochBudget,
147    /// Submitted work reached its fence-protected byte budget.
148    #[error("upload in-flight budget exhausted")]
149    InFlightBudget,
150    /// Staging bytes remain occupied by older work.
151    #[error("upload staging ring is full")]
152    RingFull,
153    /// All ticket records are in use.
154    #[error("upload ticket capacity is exhausted")]
155    TicketCapacity,
156    /// Arithmetic could not represent the reservation.
157    #[error("upload reservation size overflow")]
158    SizeOverflow,
159    /// The ticket is stale or in the wrong lifecycle state.
160    #[error("upload ticket is stale or has an invalid state")]
161    InvalidTicket,
162}
163
164/// Work made reusable by one retirement call.
165#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
166pub struct Retirement {
167    /// Number of retired tickets.
168    pub tickets: usize,
169    /// Payload bytes released after completed submissions.
170    pub bytes: u64,
171}