daedalus_gpu/
handles.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::sync::Arc;
4use std::sync::atomic::{AtomicU64, Ordering};
5
6use crate::{GpuFormat, GpuMemoryLocation, GpuUsage};
7
8static NEXT_BUFFER_ID: AtomicU64 = AtomicU64::new(1);
9static NEXT_IMAGE_ID: AtomicU64 = AtomicU64::new(1);
10
11/// Opaque buffer identifier.
12///
13/// ```
14/// use daedalus_gpu::GpuBufferId;
15/// let id = GpuBufferId(1);
16/// assert_eq!(id.0, 1);
17/// ```
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub struct GpuBufferId(pub u64);
20
21/// Opaque image identifier.
22///
23/// ```
24/// use daedalus_gpu::GpuImageId;
25/// let id = GpuImageId(2);
26/// assert_eq!(id.0, 2);
27/// ```
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
29pub struct GpuImageId(pub u64);
30
31impl fmt::Display for GpuBufferId {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        write!(f, "buf-{}", self.0)
34    }
35}
36
37impl fmt::Display for GpuImageId {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        write!(f, "img-{}", self.0)
40    }
41}
42
43fn next_buffer_id() -> GpuBufferId {
44    GpuBufferId(NEXT_BUFFER_ID.fetch_add(1, Ordering::Relaxed))
45}
46
47fn next_image_id() -> GpuImageId {
48    GpuImageId(NEXT_IMAGE_ID.fetch_add(1, Ordering::Relaxed))
49}
50
51pub(crate) trait GpuDropToken: Send + Sync + fmt::Debug {}
52
53impl<T: Send + Sync + fmt::Debug> GpuDropToken for T {}
54
55/// Opaque buffer handle (no backend types).
56///
57/// ```
58/// use daedalus_gpu::{GpuBufferHandle, GpuMemoryLocation, GpuUsage};
59/// let handle = GpuBufferHandle::new(1024, GpuMemoryLocation::Cpu, GpuUsage::UPLOAD);
60/// assert_eq!(handle.size_bytes, 1024);
61/// ```
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct GpuBufferHandle {
64    pub id: GpuBufferId,
65    pub size_bytes: u64,
66    pub location: GpuMemoryLocation,
67    pub usage: GpuUsage,
68    pub label: Option<String>,
69    #[serde(skip)]
70    pub(crate) drop_token: Option<Arc<dyn GpuDropToken>>,
71}
72
73impl GpuBufferHandle {
74    /// Create a new buffer handle.
75    pub fn new(size_bytes: u64, location: GpuMemoryLocation, usage: GpuUsage) -> Self {
76        Self {
77            id: next_buffer_id(),
78            size_bytes,
79            location,
80            usage,
81            label: None,
82            drop_token: None,
83        }
84    }
85
86    /// Attach a label for diagnostics.
87    pub fn with_label(mut self, label: impl Into<String>) -> Self {
88        self.label = Some(label.into());
89        self
90    }
91}
92
93impl Drop for GpuBufferHandle {
94    fn drop(&mut self) {
95        // This token is used for its Drop side-effects (backend resource cleanup).
96        // Touch it so `-D dead-code` doesn't treat it as unused.
97        let _ = self.drop_token.take();
98    }
99}
100
101impl PartialEq for GpuBufferHandle {
102    fn eq(&self, other: &Self) -> bool {
103        self.id == other.id
104            && self.size_bytes == other.size_bytes
105            && self.location == other.location
106            && self.usage == other.usage
107            && self.label == other.label
108    }
109}
110
111impl Eq for GpuBufferHandle {}
112
113/// Opaque image/texture handle (no backend types).
114///
115/// ```
116/// use daedalus_gpu::{GpuImageHandle, GpuFormat, GpuMemoryLocation, GpuUsage};
117/// let handle = GpuImageHandle::new(GpuFormat::Rgba8Unorm, 8, 8, GpuMemoryLocation::Gpu, GpuUsage::STORAGE);
118/// assert_eq!(handle.width, 8);
119/// ```
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct GpuImageHandle {
122    pub id: GpuImageId,
123    pub format: GpuFormat,
124    pub width: u32,
125    pub height: u32,
126    pub location: GpuMemoryLocation,
127    pub usage: GpuUsage,
128    pub label: Option<String>,
129    #[serde(skip)]
130    pub(crate) drop_token: Option<Arc<dyn GpuDropToken>>,
131}
132
133impl GpuImageHandle {
134    /// Create a new image handle.
135    pub fn new(
136        format: GpuFormat,
137        width: u32,
138        height: u32,
139        location: GpuMemoryLocation,
140        usage: GpuUsage,
141    ) -> Self {
142        Self {
143            id: next_image_id(),
144            format,
145            width,
146            height,
147            location,
148            usage,
149            label: None,
150            drop_token: None,
151        }
152    }
153
154    /// Attach a label for diagnostics.
155    pub fn with_label(mut self, label: impl Into<String>) -> Self {
156        self.label = Some(label.into());
157        self
158    }
159}
160
161impl Drop for GpuImageHandle {
162    fn drop(&mut self) {
163        // This token is used for its Drop side-effects (backend resource cleanup).
164        // Touch it so `-D dead-code` doesn't treat it as unused.
165        let _ = self.drop_token.take();
166    }
167}
168
169impl PartialEq for GpuImageHandle {
170    fn eq(&self, other: &Self) -> bool {
171        self.id == other.id
172            && self.format == other.format
173            && self.width == other.width
174            && self.height == other.height
175            && self.location == other.location
176            && self.usage == other.usage
177            && self.label == other.label
178    }
179}
180
181impl Eq for GpuImageHandle {}