wgpu_examples/hello_windows/
mod.rs

1#![cfg_attr(target_arch = "wasm32", allow(dead_code))]
2
3use std::{collections::HashMap, sync::Arc};
4use winit::{
5    event::{Event, WindowEvent},
6    event_loop::EventLoop,
7    window::{Window, WindowId},
8};
9
10struct ViewportDesc {
11    window: Arc<Window>,
12    background: wgpu::Color,
13    surface: wgpu::Surface<'static>,
14}
15
16struct Viewport {
17    desc: ViewportDesc,
18    config: wgpu::SurfaceConfiguration,
19}
20
21impl ViewportDesc {
22    fn new(window: Arc<Window>, background: wgpu::Color, instance: &wgpu::Instance) -> Self {
23        let surface = instance.create_surface(window.clone()).unwrap();
24        Self {
25            window,
26            background,
27            surface,
28        }
29    }
30
31    fn build(self, adapter: &wgpu::Adapter, device: &wgpu::Device) -> Viewport {
32        let size = self.window.inner_size();
33        let config = self
34            .surface
35            .get_default_config(adapter, size.width, size.height)
36            .unwrap();
37        self.surface.configure(device, &config);
38        Viewport { desc: self, config }
39    }
40}
41
42impl Viewport {
43    fn resize(&mut self, device: &wgpu::Device, size: winit::dpi::PhysicalSize<u32>) {
44        self.config.width = size.width;
45        self.config.height = size.height;
46        self.desc.surface.configure(device, &self.config);
47    }
48    fn get_current_texture(&mut self) -> wgpu::SurfaceTexture {
49        self.desc
50            .surface
51            .get_current_texture()
52            .expect("Failed to acquire next swap chain texture")
53    }
54}
55
56async fn run(event_loop: EventLoop<()>, viewports: Vec<(Arc<Window>, wgpu::Color)>) {
57    let instance = wgpu::Instance::default();
58    let viewports: Vec<_> = viewports
59        .into_iter()
60        .map(|(window, color)| ViewportDesc::new(window, color, &instance))
61        .collect();
62    let adapter = instance
63        .request_adapter(&wgpu::RequestAdapterOptions {
64            // Request an adapter which can render to our surface
65            compatible_surface: viewports.first().map(|desc| &desc.surface),
66            ..Default::default()
67        })
68        .await
69        .expect("Failed to find an appropriate adapter");
70
71    // Create the logical device and command queue
72    let (device, queue) = adapter
73        .request_device(&wgpu::DeviceDescriptor {
74            label: None,
75            required_features: wgpu::Features::empty(),
76            required_limits: wgpu::Limits::downlevel_defaults(),
77            experimental_features: wgpu::ExperimentalFeatures::disabled(),
78            memory_hints: wgpu::MemoryHints::MemoryUsage,
79            trace: wgpu::Trace::Off,
80        })
81        .await
82        .expect("Failed to create device");
83
84    let mut viewports: HashMap<WindowId, Viewport> = viewports
85        .into_iter()
86        .map(|desc| (desc.window.id(), desc.build(&adapter, &device)))
87        .collect();
88
89    event_loop
90        .run(move |event, target| {
91            // Have the closure take ownership of the resources.
92            // `event_loop.run` never returns, therefore we must do this to ensure
93            // the resources are properly cleaned up.
94            let _ = (&instance, &adapter);
95
96            if let Event::WindowEvent { window_id, event } = event {
97                match event {
98                    WindowEvent::Resized(new_size) => {
99                        // Recreate the swap chain with the new size
100                        if let Some(viewport) = viewports.get_mut(&window_id) {
101                            viewport.resize(&device, new_size);
102                            // On macos the window needs to be redrawn manually after resizing
103                            viewport.desc.window.request_redraw();
104                        }
105                    }
106                    WindowEvent::RedrawRequested => {
107                        if let Some(viewport) = viewports.get_mut(&window_id) {
108                            let frame = viewport.get_current_texture();
109                            let view = frame
110                                .texture
111                                .create_view(&wgpu::TextureViewDescriptor::default());
112                            let mut encoder =
113                                device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
114                                    label: None,
115                                });
116                            {
117                                let _rpass =
118                                    encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
119                                        label: None,
120                                        color_attachments: &[Some(
121                                            wgpu::RenderPassColorAttachment {
122                                                view: &view,
123                                                depth_slice: None,
124                                                resolve_target: None,
125                                                ops: wgpu::Operations {
126                                                    load: wgpu::LoadOp::Clear(
127                                                        viewport.desc.background,
128                                                    ),
129                                                    store: wgpu::StoreOp::Store,
130                                                },
131                                            },
132                                        )],
133                                        depth_stencil_attachment: None,
134                                        timestamp_writes: None,
135                                        occlusion_query_set: None,
136                                        multiview_mask: None,
137                                    });
138                            }
139
140                            queue.submit(Some(encoder.finish()));
141                            viewport.desc.window.pre_present_notify();
142                            frame.present();
143                        }
144                    }
145                    WindowEvent::CloseRequested => {
146                        viewports.remove(&window_id);
147                        if viewports.is_empty() {
148                            target.exit();
149                        }
150                    }
151                    _ => {}
152                }
153            }
154        })
155        .unwrap();
156}
157
158pub fn main() {
159    #[cfg(not(target_arch = "wasm32"))]
160    {
161        const WINDOW_SIZE: u32 = 128;
162        const WINDOW_PADDING: u32 = 16;
163        const WINDOW_TITLEBAR: u32 = 32;
164        const WINDOW_OFFSET: u32 = WINDOW_SIZE + WINDOW_PADDING;
165        const ROWS: u32 = 4;
166        const COLUMNS: u32 = 4;
167
168        let event_loop = EventLoop::new().unwrap();
169        let mut viewports = Vec::with_capacity((ROWS * COLUMNS) as usize);
170        for row in 0..ROWS {
171            for column in 0..COLUMNS {
172                let window = winit::window::WindowBuilder::new()
173                    .with_title(format!("x{column}y{row}"))
174                    .with_inner_size(winit::dpi::PhysicalSize::new(WINDOW_SIZE, WINDOW_SIZE))
175                    .build(&event_loop)
176                    .unwrap();
177                let window = Arc::new(window);
178                window.set_outer_position(winit::dpi::PhysicalPosition::new(
179                    WINDOW_PADDING + column * WINDOW_OFFSET,
180                    WINDOW_PADDING + row * (WINDOW_OFFSET + WINDOW_TITLEBAR),
181                ));
182                fn frac(index: u32, max: u32) -> f64 {
183                    index as f64 / max as f64
184                }
185                viewports.push((
186                    window,
187                    wgpu::Color {
188                        r: frac(row, ROWS),
189                        g: 0.5 - frac(row * column, ROWS * COLUMNS) * 0.5,
190                        b: frac(column, COLUMNS),
191                        a: 1.0,
192                    },
193                ))
194            }
195        }
196
197        env_logger::init();
198        pollster::block_on(run(event_loop, viewports));
199    }
200    #[cfg(target_arch = "wasm32")]
201    {
202        std::panic::set_hook(Box::new(console_error_panic_hook::hook));
203        panic!("wasm32 is not supported")
204    }
205}