Skip to main content

astrelis_test_utils/
gpu_types.rs

1//! GPU resource wrappers that can be real or mock.
2//!
3//! These types wrap WGPU resources and allow for both real GPU operations
4//! and mock implementations for testing.
5
6use wgpu;
7
8/// Wrapper around GPU buffer that can be real or mock.
9///
10/// # Design Pattern: Opaque Wrapper
11///
12/// This type hides whether it contains a real `wgpu::Buffer` or a mock.
13/// Users hold owned `GpuBuffer`, which is cheap to clone (Arc inside).
14///
15/// # Benefits
16/// 1. No lifetimes - users own the buffer
17/// 2. Can be mock or real without user knowing
18/// 3. Clone is cheap (Arc internally for real buffers)
19#[derive(Clone, Debug)]
20pub struct GpuBuffer {
21    inner: GpuBufferInner,
22}
23
24#[derive(Clone, Debug)]
25enum GpuBufferInner {
26    Real(wgpu::Buffer),
27    #[cfg(feature = "mock")]
28    Mock {
29        id: usize,
30        size: u64,
31    },
32}
33
34impl GpuBuffer {
35    /// Create from real WGPU buffer
36    pub fn from_wgpu(buffer: wgpu::Buffer) -> Self {
37        Self {
38            inner: GpuBufferInner::Real(buffer),
39        }
40    }
41
42    /// Create mock buffer (for testing)
43    #[cfg(feature = "mock")]
44    pub fn mock(id: usize, size: u64) -> Self {
45        Self {
46            inner: GpuBufferInner::Mock { id, size },
47        }
48    }
49
50    /// Get the underlying wgpu::Buffer (if real)
51    ///
52    /// # Panics
53    /// Panics if this is a mock buffer (test code should never call this)
54    pub fn as_wgpu(&self) -> &wgpu::Buffer {
55        match &self.inner {
56            GpuBufferInner::Real(buffer) => buffer,
57            #[cfg(feature = "mock")]
58            GpuBufferInner::Mock { .. } => {
59                panic!(
60                    "Attempted to get wgpu::Buffer from mock buffer - this is a test-only buffer"
61                )
62            }
63        }
64    }
65
66    /// Check if this is a mock (useful in tests)
67    #[cfg(feature = "mock")]
68    pub fn is_mock(&self) -> bool {
69        matches!(self.inner, GpuBufferInner::Mock { .. })
70    }
71
72    /// Get mock ID (for test assertions)
73    #[cfg(feature = "mock")]
74    pub fn mock_id(&self) -> Option<usize> {
75        match &self.inner {
76            GpuBufferInner::Mock { id, .. } => Some(*id),
77            _ => None,
78        }
79    }
80}
81
82/// Wrapper around GPU texture that can be real or mock.
83#[derive(Clone, Debug)]
84pub struct GpuTexture {
85    inner: GpuTextureInner,
86}
87
88#[derive(Clone, Debug)]
89enum GpuTextureInner {
90    Real(wgpu::Texture),
91    #[cfg(feature = "mock")]
92    Mock {
93        id: usize,
94        width: u32,
95        height: u32,
96        format: wgpu::TextureFormat,
97    },
98}
99
100impl GpuTexture {
101    /// Create from real WGPU texture
102    pub fn from_wgpu(texture: wgpu::Texture) -> Self {
103        Self {
104            inner: GpuTextureInner::Real(texture),
105        }
106    }
107
108    /// Create mock texture (for testing)
109    #[cfg(feature = "mock")]
110    pub fn mock(id: usize, width: u32, height: u32, format: wgpu::TextureFormat) -> Self {
111        Self {
112            inner: GpuTextureInner::Mock {
113                id,
114                width,
115                height,
116                format,
117            },
118        }
119    }
120
121    /// Get the underlying wgpu::Texture (if real)
122    ///
123    /// # Panics
124    /// Panics if this is a mock texture
125    pub fn as_wgpu(&self) -> &wgpu::Texture {
126        match &self.inner {
127            GpuTextureInner::Real(texture) => texture,
128            #[cfg(feature = "mock")]
129            GpuTextureInner::Mock { .. } => {
130                panic!("Attempted to get wgpu::Texture from mock texture")
131            }
132        }
133    }
134
135    /// Check if this is a mock
136    #[cfg(feature = "mock")]
137    pub fn is_mock(&self) -> bool {
138        matches!(self.inner, GpuTextureInner::Mock { .. })
139    }
140
141    /// Get mock ID (for test assertions)
142    #[cfg(feature = "mock")]
143    pub fn mock_id(&self) -> Option<usize> {
144        match &self.inner {
145            GpuTextureInner::Mock { id, .. } => Some(*id),
146            _ => None,
147        }
148    }
149}
150
151/// Wrapper around GPU shader module that can be real or mock.
152#[derive(Clone, Debug)]
153pub struct GpuShaderModule {
154    inner: GpuShaderModuleInner,
155}
156
157#[derive(Clone, Debug)]
158enum GpuShaderModuleInner {
159    Real(wgpu::ShaderModule),
160    #[cfg(feature = "mock")]
161    Mock {
162        id: usize,
163    },
164}
165
166impl GpuShaderModule {
167    /// Create from real WGPU shader module
168    pub fn from_wgpu(module: wgpu::ShaderModule) -> Self {
169        Self {
170            inner: GpuShaderModuleInner::Real(module),
171        }
172    }
173
174    /// Create mock shader module (for testing)
175    #[cfg(feature = "mock")]
176    pub fn mock(id: usize) -> Self {
177        Self {
178            inner: GpuShaderModuleInner::Mock { id },
179        }
180    }
181
182    /// Get the underlying wgpu::ShaderModule (if real)
183    pub fn as_wgpu(&self) -> &wgpu::ShaderModule {
184        match &self.inner {
185            GpuShaderModuleInner::Real(module) => module,
186            #[cfg(feature = "mock")]
187            GpuShaderModuleInner::Mock { .. } => {
188                panic!("Attempted to get wgpu::ShaderModule from mock")
189            }
190        }
191    }
192
193    /// Check if this is a mock
194    #[cfg(feature = "mock")]
195    pub fn is_mock(&self) -> bool {
196        matches!(self.inner, GpuShaderModuleInner::Mock { .. })
197    }
198}
199
200/// Wrapper around GPU render pipeline that can be real or mock.
201#[derive(Clone, Debug)]
202pub struct GpuRenderPipeline {
203    inner: GpuRenderPipelineInner,
204}
205
206#[derive(Clone, Debug)]
207enum GpuRenderPipelineInner {
208    Real(wgpu::RenderPipeline),
209    #[cfg(feature = "mock")]
210    Mock {
211        id: usize,
212    },
213}
214
215impl GpuRenderPipeline {
216    /// Create from real WGPU render pipeline
217    pub fn from_wgpu(pipeline: wgpu::RenderPipeline) -> Self {
218        Self {
219            inner: GpuRenderPipelineInner::Real(pipeline),
220        }
221    }
222
223    /// Create mock render pipeline (for testing)
224    #[cfg(feature = "mock")]
225    pub fn mock(id: usize) -> Self {
226        Self {
227            inner: GpuRenderPipelineInner::Mock { id },
228        }
229    }
230
231    /// Get the underlying wgpu::RenderPipeline (if real)
232    pub fn as_wgpu(&self) -> &wgpu::RenderPipeline {
233        match &self.inner {
234            GpuRenderPipelineInner::Real(pipeline) => pipeline,
235            #[cfg(feature = "mock")]
236            GpuRenderPipelineInner::Mock { .. } => {
237                panic!("Attempted to get wgpu::RenderPipeline from mock")
238            }
239        }
240    }
241
242    /// Check if this is a mock
243    #[cfg(feature = "mock")]
244    pub fn is_mock(&self) -> bool {
245        matches!(self.inner, GpuRenderPipelineInner::Mock { .. })
246    }
247}
248
249/// Wrapper around GPU compute pipeline that can be real or mock.
250#[derive(Clone, Debug)]
251pub struct GpuComputePipeline {
252    inner: GpuComputePipelineInner,
253}
254
255#[derive(Clone, Debug)]
256enum GpuComputePipelineInner {
257    Real(wgpu::ComputePipeline),
258    #[cfg(feature = "mock")]
259    Mock {
260        id: usize,
261    },
262}
263
264impl GpuComputePipeline {
265    /// Create from real WGPU compute pipeline
266    pub fn from_wgpu(pipeline: wgpu::ComputePipeline) -> Self {
267        Self {
268            inner: GpuComputePipelineInner::Real(pipeline),
269        }
270    }
271
272    /// Create mock compute pipeline (for testing)
273    #[cfg(feature = "mock")]
274    pub fn mock(id: usize) -> Self {
275        Self {
276            inner: GpuComputePipelineInner::Mock { id },
277        }
278    }
279
280    /// Get the underlying wgpu::ComputePipeline (if real)
281    pub fn as_wgpu(&self) -> &wgpu::ComputePipeline {
282        match &self.inner {
283            GpuComputePipelineInner::Real(pipeline) => pipeline,
284            #[cfg(feature = "mock")]
285            GpuComputePipelineInner::Mock { .. } => {
286                panic!("Attempted to get wgpu::ComputePipeline from mock")
287            }
288        }
289    }
290
291    /// Check if this is a mock
292    #[cfg(feature = "mock")]
293    pub fn is_mock(&self) -> bool {
294        matches!(self.inner, GpuComputePipelineInner::Mock { .. })
295    }
296}
297
298/// Wrapper around GPU bind group layout that can be real or mock.
299#[derive(Clone, Debug)]
300pub struct GpuBindGroupLayout {
301    inner: GpuBindGroupLayoutInner,
302}
303
304#[derive(Clone, Debug)]
305enum GpuBindGroupLayoutInner {
306    Real(wgpu::BindGroupLayout),
307    #[cfg(feature = "mock")]
308    Mock {
309        id: usize,
310    },
311}
312
313impl GpuBindGroupLayout {
314    /// Create from real WGPU bind group layout
315    pub fn from_wgpu(layout: wgpu::BindGroupLayout) -> Self {
316        Self {
317            inner: GpuBindGroupLayoutInner::Real(layout),
318        }
319    }
320
321    /// Create mock bind group layout (for testing)
322    #[cfg(feature = "mock")]
323    pub fn mock(id: usize) -> Self {
324        Self {
325            inner: GpuBindGroupLayoutInner::Mock { id },
326        }
327    }
328
329    /// Get the underlying wgpu::BindGroupLayout (if real)
330    pub fn as_wgpu(&self) -> &wgpu::BindGroupLayout {
331        match &self.inner {
332            GpuBindGroupLayoutInner::Real(layout) => layout,
333            #[cfg(feature = "mock")]
334            GpuBindGroupLayoutInner::Mock { .. } => {
335                panic!("Attempted to get wgpu::BindGroupLayout from mock")
336            }
337        }
338    }
339
340    /// Check if this is a mock
341    #[cfg(feature = "mock")]
342    pub fn is_mock(&self) -> bool {
343        matches!(self.inner, GpuBindGroupLayoutInner::Mock { .. })
344    }
345}
346
347/// Wrapper around GPU bind group that can be real or mock.
348#[derive(Clone, Debug)]
349pub struct GpuBindGroup {
350    inner: GpuBindGroupInner,
351}
352
353#[derive(Clone, Debug)]
354enum GpuBindGroupInner {
355    Real(wgpu::BindGroup),
356    #[cfg(feature = "mock")]
357    Mock {
358        id: usize,
359    },
360}
361
362impl GpuBindGroup {
363    /// Create from real WGPU bind group
364    pub fn from_wgpu(bind_group: wgpu::BindGroup) -> Self {
365        Self {
366            inner: GpuBindGroupInner::Real(bind_group),
367        }
368    }
369
370    /// Create mock bind group (for testing)
371    #[cfg(feature = "mock")]
372    pub fn mock(id: usize) -> Self {
373        Self {
374            inner: GpuBindGroupInner::Mock { id },
375        }
376    }
377
378    /// Get the underlying wgpu::BindGroup (if real)
379    pub fn as_wgpu(&self) -> &wgpu::BindGroup {
380        match &self.inner {
381            GpuBindGroupInner::Real(bind_group) => bind_group,
382            #[cfg(feature = "mock")]
383            GpuBindGroupInner::Mock { .. } => {
384                panic!("Attempted to get wgpu::BindGroup from mock")
385            }
386        }
387    }
388
389    /// Check if this is a mock
390    #[cfg(feature = "mock")]
391    pub fn is_mock(&self) -> bool {
392        matches!(self.inner, GpuBindGroupInner::Mock { .. })
393    }
394}
395
396/// Wrapper around GPU sampler that can be real or mock.
397#[derive(Clone, Debug)]
398pub struct GpuSampler {
399    inner: GpuSamplerInner,
400}
401
402#[derive(Clone, Debug)]
403enum GpuSamplerInner {
404    Real(wgpu::Sampler),
405    #[cfg(feature = "mock")]
406    Mock {
407        id: usize,
408    },
409}
410
411impl GpuSampler {
412    /// Create from real WGPU sampler
413    pub fn from_wgpu(sampler: wgpu::Sampler) -> Self {
414        Self {
415            inner: GpuSamplerInner::Real(sampler),
416        }
417    }
418
419    /// Create mock sampler (for testing)
420    #[cfg(feature = "mock")]
421    pub fn mock(id: usize) -> Self {
422        Self {
423            inner: GpuSamplerInner::Mock { id },
424        }
425    }
426
427    /// Get the underlying wgpu::Sampler (if real)
428    pub fn as_wgpu(&self) -> &wgpu::Sampler {
429        match &self.inner {
430            GpuSamplerInner::Real(sampler) => sampler,
431            #[cfg(feature = "mock")]
432            GpuSamplerInner::Mock { .. } => {
433                panic!("Attempted to get wgpu::Sampler from mock")
434            }
435        }
436    }
437
438    /// Check if this is a mock
439    #[cfg(feature = "mock")]
440    pub fn is_mock(&self) -> bool {
441        matches!(self.inner, GpuSamplerInner::Mock { .. })
442    }
443}