Skip to main content

oxigdal_gpu/
webgpu_compat.rs

1//! WebGPU compatibility layer for WASM targets.
2//!
3//! On `wasm32` targets, `wgpu` uses the WebGPU API.
4//! This module provides compile-time shader registration and capability
5//! structs that describe conservative GPU limits for cross-platform code.
6
7/// Shader source registry for all built-in compute shaders.
8///
9/// Shaders are embedded at compile time via `include_str!`, so they are
10/// always available regardless of the file-system at runtime.
11pub struct ShaderRegistry;
12
13impl ShaderRegistry {
14    /// Return the WGSL source for a named shader, or `None` if unknown.
15    ///
16    /// # Known names
17    /// - `"reproject"` — raster reprojection
18    /// - `"raster_algebra"` — element-wise band math
19    /// - `"hillshade"` — terrain hillshade
20    pub fn get(name: &str) -> Option<&'static str> {
21        match name {
22            "reproject" => Some(include_str!("shaders/reproject.wgsl")),
23            "raster_algebra" => Some(include_str!("shaders/raster_algebra.wgsl")),
24            "hillshade" => Some(include_str!("shaders/hillshade.wgsl")),
25            _ => None,
26        }
27    }
28
29    /// Return the list of all available shader names.
30    pub fn list() -> &'static [&'static str] {
31        &["reproject", "raster_algebra", "hillshade"]
32    }
33}
34
35/// GPU feature capabilities descriptor.
36///
37/// Use [`GpuCapabilities::default`] for native targets and
38/// [`GpuCapabilities::webgpu_conservative`] when targeting WebGPU / WASM.
39#[derive(Debug, Clone, PartialEq)]
40pub struct GpuCapabilities {
41    /// True when compute shaders are supported.
42    pub has_compute: bool,
43    /// True when `texture_float` / `float32-filterable` is available.
44    pub has_texture_float: bool,
45    /// Maximum total invocations per workgroup (product of x * y * z sizes).
46    pub max_workgroup_size: u32,
47    /// Maximum size of a single buffer in bytes.
48    pub max_buffer_size: u64,
49}
50
51impl Default for GpuCapabilities {
52    fn default() -> Self {
53        Self {
54            has_compute: true,
55            has_texture_float: true,
56            max_workgroup_size: 256,
57            max_buffer_size: 256 * 1024 * 1024, // 256 MiB
58        }
59    }
60}
61
62impl GpuCapabilities {
63    /// Conservative capabilities for a WebGPU (WASM) context.
64    ///
65    /// Buffer size is limited to 128 MiB as mandated by the WebGPU
66    /// specification minimum.
67    pub fn webgpu_conservative() -> Self {
68        Self {
69            has_compute: true,
70            has_texture_float: true,
71            max_workgroup_size: 256,
72            max_buffer_size: 128 * 1024 * 1024, // 128 MiB
73        }
74    }
75
76    /// Returns `true` when `size` bytes fit within the maximum buffer limit.
77    pub fn validate_buffer_size(&self, size: u64) -> bool {
78        size <= self.max_buffer_size
79    }
80
81    /// Returns `true` when `size` invocations fit within the workgroup limit.
82    pub fn validate_workgroup(&self, size: u32) -> bool {
83        size <= self.max_workgroup_size
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_shader_registry_known_names() {
93        for name in ShaderRegistry::list() {
94            assert!(
95                ShaderRegistry::get(name).is_some(),
96                "expected shader '{}' to be registered",
97                name
98            );
99        }
100    }
101
102    #[test]
103    fn test_shader_registry_unknown() {
104        assert!(ShaderRegistry::get("nonexistent_shader").is_none());
105    }
106
107    #[test]
108    fn test_capabilities_default() {
109        let caps = GpuCapabilities::default();
110        assert!(caps.has_compute);
111        assert_eq!(caps.max_workgroup_size, 256);
112        assert_eq!(caps.max_buffer_size, 256 * 1024 * 1024);
113    }
114
115    #[test]
116    fn test_capabilities_webgpu() {
117        let caps = GpuCapabilities::webgpu_conservative();
118        assert_eq!(caps.max_buffer_size, 128 * 1024 * 1024);
119    }
120}