daedalus_gpu/
noop.rs

1use std::sync::Mutex;
2
3use crate::handles::{GpuBufferHandle, GpuImageHandle};
4use crate::traits::GpuBackend;
5use crate::{
6    GpuAdapterInfo, GpuBackendKind, GpuCapabilities, GpuError, GpuFormat, GpuFormatFeatures,
7    GpuImageRequest, GpuMemoryLocation, GpuOptions, GpuRequest, buffer::TransferStats,
8};
9
10/// Noop backend: deterministic, does nothing.
11#[derive(Default)]
12pub struct NoopBackend {
13    stats: Mutex<TransferStats>,
14}
15
16impl GpuBackend for NoopBackend {
17    fn kind(&self) -> GpuBackendKind {
18        GpuBackendKind::Noop
19    }
20
21    fn as_any(&self) -> &dyn std::any::Any {
22        self
23    }
24
25    fn adapter_info(&self) -> GpuAdapterInfo {
26        GpuAdapterInfo {
27            name: "noop".into(),
28            backend: GpuBackendKind::Noop,
29            device_id: None,
30            vendor_id: None,
31        }
32    }
33
34    fn capabilities(&self) -> GpuCapabilities {
35        GpuCapabilities {
36            supported_formats: vec![GpuFormat::R8Unorm, GpuFormat::Rgba8Unorm],
37            format_features: vec![
38                GpuFormatFeatures {
39                    format: GpuFormat::R8Unorm,
40                    sampleable: true,
41                    renderable: true,
42                    storage: false,
43                    max_samples: 1,
44                },
45                GpuFormatFeatures {
46                    format: GpuFormat::Rgba8Unorm,
47                    sampleable: true,
48                    renderable: true,
49                    storage: false,
50                    max_samples: 1,
51                },
52            ],
53            format_blocks: vec![
54                crate::GpuBlockInfo {
55                    format: GpuFormat::R8Unorm,
56                    block_width: 1,
57                    block_height: 1,
58                    bytes_per_block: 1,
59                },
60                crate::GpuBlockInfo {
61                    format: GpuFormat::Rgba8Unorm,
62                    block_width: 1,
63                    block_height: 1,
64                    bytes_per_block: 4,
65                },
66            ],
67            max_buffer_size: 0,
68            max_texture_dimension: 0,
69            max_texture_samples: 1,
70            staging_alignment: 1,
71            max_inflight_copies: 1,
72            queue_count: 1,
73            min_buffer_copy_offset_alignment: 1,
74            bytes_per_row_alignment: 1,
75            rows_per_image_alignment: 1,
76            has_transfer_queue: false,
77        }
78    }
79
80    fn select_adapter(&self, _opts: &GpuOptions) -> Result<GpuAdapterInfo, GpuError> {
81        Ok(self.adapter_info())
82    }
83
84    fn create_buffer(&self, req: &GpuRequest) -> Result<GpuBufferHandle, GpuError> {
85        if req.usage.is_empty() {
86            return Err(GpuError::Unsupported);
87        }
88        let mut stats = self.stats.lock().expect("noop stats lock");
89        stats.record_upload(req.size_bytes);
90        Ok(GpuBufferHandle::new(
91            req.size_bytes,
92            GpuMemoryLocation::Cpu,
93            req.usage,
94        ))
95    }
96
97    fn create_image(&self, req: &GpuImageRequest) -> Result<GpuImageHandle, GpuError> {
98        if req.usage.is_empty() {
99            return Err(GpuError::Unsupported);
100        }
101        let mut stats = self.stats.lock().expect("noop stats lock");
102        let bpp = crate::format_bytes_per_pixel(req.format).unwrap_or(4) as u64;
103        let bytes = (req.width as u64) * (req.height as u64) * bpp;
104        stats.record_upload(bytes);
105        Ok(GpuImageHandle::new(
106            req.format,
107            req.width,
108            req.height,
109            GpuMemoryLocation::Cpu,
110            req.usage,
111        ))
112    }
113
114    fn stats(&self) -> TransferStats {
115        *self.stats.lock().expect("noop stats lock")
116    }
117
118    fn take_stats(&self) -> TransferStats {
119        self.stats.lock().expect("noop stats lock").take()
120    }
121
122    fn record_download(&self, bytes: u64) {
123        let mut stats = self.stats.lock().expect("noop stats lock");
124        stats.record_download(bytes);
125    }
126
127    fn upload_texture(
128        &self,
129        req: &GpuImageRequest,
130        data: &[u8],
131    ) -> Result<GpuImageHandle, GpuError> {
132        let handle = self.create_image(req)?;
133        let mut stats = self.stats.lock().expect("noop stats lock");
134        stats.record_upload(data.len() as u64);
135        Ok(handle)
136    }
137
138    fn read_texture(&self, handle: &GpuImageHandle) -> Result<Vec<u8>, GpuError> {
139        let bpp = crate::format_bytes_per_pixel(handle.format).unwrap_or(4) as usize;
140        let bytes = (handle.width as usize) * (handle.height as usize) * bpp;
141        self.record_download(bytes as u64);
142        Ok(vec![0; bytes])
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149    use crate::GpuUsage;
150
151    #[test]
152    fn noop_creates_handles() {
153        let backend = NoopBackend::default();
154        let buf = backend
155            .create_buffer(&GpuRequest {
156                usage: GpuUsage::UPLOAD,
157                format: None,
158                size_bytes: 128,
159            })
160            .unwrap();
161        assert_eq!(buf.size_bytes, 128);
162        let img = backend
163            .create_image(&GpuImageRequest {
164                format: GpuFormat::Rgba8Unorm,
165                width: 1,
166                height: 1,
167                samples: 1,
168                usage: GpuUsage::RENDER_TARGET,
169            })
170            .unwrap();
171        assert_eq!(img.format, GpuFormat::Rgba8Unorm);
172    }
173}