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