Skip to main content

dicomview_gpu/
surface.rs

1//! WASM canvas surface management.
2
3use thiserror::Error;
4
5/// A configured wgpu surface bound to one HTML canvas element.
6#[derive(Debug)]
7pub struct CanvasSurface {
8    /// The underlying wgpu surface.
9    pub surface: wgpu::Surface<'static>,
10    /// The current surface configuration.
11    pub config: wgpu::SurfaceConfiguration,
12    /// The presentation format selected for the surface.
13    pub format: wgpu::TextureFormat,
14    /// Current logical size in pixels.
15    pub size: (u32, u32),
16}
17
18/// Surface-management errors.
19#[derive(Debug, Error)]
20pub enum CanvasSurfaceError {
21    /// Surface creation failed.
22    #[error("failed to create canvas surface: {0}")]
23    Create(String),
24    /// No supported surface format was available.
25    #[error("surface reports no supported formats")]
26    NoSupportedFormat,
27}
28
29impl CanvasSurface {
30    /// Creates and configures a surface for a browser canvas.
31    #[cfg(target_arch = "wasm32")]
32    pub fn from_canvas(
33        instance: &wgpu::Instance,
34        adapter: &wgpu::Adapter,
35        device: &wgpu::Device,
36        canvas: web_sys::HtmlCanvasElement,
37        preferred_format: Option<wgpu::TextureFormat>,
38    ) -> Result<Self, CanvasSurfaceError> {
39        let size = (canvas.width().max(1), canvas.height().max(1));
40        let surface = instance
41            .create_surface(wgpu::SurfaceTarget::Canvas(canvas))
42            .map_err(|error| CanvasSurfaceError::Create(error.to_string()))?;
43        let capabilities = surface.get_capabilities(adapter);
44        let format = preferred_format
45            .filter(|format| capabilities.formats.contains(format))
46            .or_else(|| {
47                capabilities.formats.iter().copied().find(|format| {
48                    matches!(
49                        format,
50                        wgpu::TextureFormat::Bgra8Unorm
51                            | wgpu::TextureFormat::Bgra8UnormSrgb
52                            | wgpu::TextureFormat::Rgba8Unorm
53                            | wgpu::TextureFormat::Rgba8UnormSrgb
54                    )
55                })
56            })
57            .or_else(|| capabilities.formats.first().copied())
58            .ok_or(CanvasSurfaceError::NoSupportedFormat)?;
59        let present_mode = capabilities
60            .present_modes
61            .iter()
62            .copied()
63            .find(|mode| *mode == wgpu::PresentMode::AutoVsync)
64            .unwrap_or(capabilities.present_modes[0]);
65        let alpha_mode = capabilities.alpha_modes[0];
66        let config = wgpu::SurfaceConfiguration {
67            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
68            format,
69            width: size.0,
70            height: size.1,
71            present_mode,
72            alpha_mode,
73            view_formats: vec![],
74            desired_maximum_frame_latency: 2,
75        };
76        surface.configure(device, &config);
77
78        Ok(Self {
79            surface,
80            config,
81            format,
82            size,
83        })
84    }
85
86    /// Resizes and reconfigures the surface.
87    pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
88        self.size = (width.max(1), height.max(1));
89        self.config.width = self.size.0;
90        self.config.height = self.size.1;
91        self.surface.configure(device, &self.config);
92    }
93}