1use thiserror::Error;
4
5#[derive(Debug)]
7pub struct CanvasSurface {
8 pub surface: wgpu::Surface<'static>,
10 pub config: wgpu::SurfaceConfiguration,
12 pub format: wgpu::TextureFormat,
14 pub size: (u32, u32),
16}
17
18#[derive(Debug, Error)]
20pub enum CanvasSurfaceError {
21 #[error("failed to create canvas surface: {0}")]
23 Create(String),
24 #[error("surface reports no supported formats")]
26 NoSupportedFormat,
27}
28
29impl CanvasSurface {
30 #[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 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}