wgpu_examples/hello_triangle/
mod.rs

1use std::borrow::Cow;
2use winit::{
3    event::{Event, WindowEvent},
4    event_loop::EventLoop,
5    window::Window,
6};
7
8async fn run(event_loop: EventLoop<()>, window: Window) {
9    let mut size = window.inner_size();
10    size.width = size.width.max(1);
11    size.height = size.height.max(1);
12
13    let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::from_env_or_default());
14
15    let surface = instance.create_surface(&window).unwrap();
16    let adapter = instance
17        .request_adapter(&wgpu::RequestAdapterOptions {
18            power_preference: wgpu::PowerPreference::default(),
19            force_fallback_adapter: false,
20            // Request an adapter which can render to our surface
21            compatible_surface: Some(&surface),
22        })
23        .await
24        .expect("Failed to find an appropriate adapter");
25
26    // Create the logical device and command queue
27    let (device, queue) = adapter
28        .request_device(&wgpu::DeviceDescriptor {
29            label: None,
30            required_features: wgpu::Features::empty(),
31            // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain.
32            required_limits: wgpu::Limits::downlevel_webgl2_defaults()
33                .using_resolution(adapter.limits()),
34            memory_hints: wgpu::MemoryHints::MemoryUsage,
35            trace: wgpu::Trace::Off,
36        })
37        .await
38        .expect("Failed to create device");
39
40    // Load the shaders from disk
41    let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
42        label: None,
43        source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
44    });
45
46    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
47        label: None,
48        bind_group_layouts: &[],
49        push_constant_ranges: &[],
50    });
51
52    let swapchain_capabilities = surface.get_capabilities(&adapter);
53    let swapchain_format = swapchain_capabilities.formats[0];
54
55    let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
56        label: None,
57        layout: Some(&pipeline_layout),
58        vertex: wgpu::VertexState {
59            module: &shader,
60            entry_point: Some("vs_main"),
61            buffers: &[],
62            compilation_options: Default::default(),
63        },
64        fragment: Some(wgpu::FragmentState {
65            module: &shader,
66            entry_point: Some("fs_main"),
67            compilation_options: Default::default(),
68            targets: &[Some(swapchain_format.into())],
69        }),
70        primitive: wgpu::PrimitiveState::default(),
71        depth_stencil: None,
72        multisample: wgpu::MultisampleState::default(),
73        multiview: None,
74        cache: None,
75    });
76
77    let mut config = surface
78        .get_default_config(&adapter, size.width, size.height)
79        .unwrap();
80    surface.configure(&device, &config);
81
82    let window = &window;
83    event_loop
84        .run(move |event, target| {
85            // Have the closure take ownership of the resources.
86            // `event_loop.run` never returns, therefore we must do this to ensure
87            // the resources are properly cleaned up.
88            let _ = (&instance, &adapter, &shader, &pipeline_layout);
89
90            if let Event::WindowEvent {
91                window_id: _,
92                event,
93            } = event
94            {
95                match event {
96                    WindowEvent::Resized(new_size) => {
97                        // Reconfigure the surface with the new size
98                        config.width = new_size.width.max(1);
99                        config.height = new_size.height.max(1);
100                        surface.configure(&device, &config);
101                        // On macos the window needs to be redrawn manually after resizing
102                        window.request_redraw();
103                    }
104                    WindowEvent::RedrawRequested => {
105                        let frame = surface
106                            .get_current_texture()
107                            .expect("Failed to acquire next swap chain texture");
108                        let view = frame
109                            .texture
110                            .create_view(&wgpu::TextureViewDescriptor::default());
111                        let mut encoder =
112                            device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
113                                label: None,
114                            });
115                        {
116                            let mut rpass =
117                                encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
118                                    label: None,
119                                    color_attachments: &[Some(wgpu::RenderPassColorAttachment {
120                                        view: &view,
121                                        depth_slice: None,
122                                        resolve_target: None,
123                                        ops: wgpu::Operations {
124                                            load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
125                                            store: wgpu::StoreOp::Store,
126                                        },
127                                    })],
128                                    depth_stencil_attachment: None,
129                                    timestamp_writes: None,
130                                    occlusion_query_set: None,
131                                });
132                            rpass.set_pipeline(&render_pipeline);
133                            rpass.draw(0..3, 0..1);
134                        }
135
136                        queue.submit(Some(encoder.finish()));
137                        window.pre_present_notify();
138                        frame.present();
139                    }
140                    WindowEvent::CloseRequested => target.exit(),
141                    _ => {}
142                };
143            }
144        })
145        .unwrap();
146}
147
148pub fn main() {
149    let event_loop = EventLoop::new().unwrap();
150    #[cfg_attr(
151        not(target_arch = "wasm32"),
152        expect(unused_mut, reason = "`wasm32` re-assigns to specify canvas")
153    )]
154    let mut builder = winit::window::WindowBuilder::new();
155    #[cfg(target_arch = "wasm32")]
156    {
157        use wasm_bindgen::JsCast;
158        use winit::platform::web::WindowBuilderExtWebSys;
159        let canvas = web_sys::window()
160            .unwrap()
161            .document()
162            .unwrap()
163            .get_element_by_id("canvas")
164            .unwrap()
165            .dyn_into::<web_sys::HtmlCanvasElement>()
166            .unwrap();
167        builder = builder.with_canvas(Some(canvas));
168    }
169    let window = builder.build(&event_loop).unwrap();
170
171    #[cfg(not(target_arch = "wasm32"))]
172    {
173        env_logger::init();
174        pollster::block_on(run(event_loop, window));
175    }
176    #[cfg(target_arch = "wasm32")]
177    {
178        std::panic::set_hook(Box::new(console_error_panic_hook::hook));
179        console_log::init().expect("could not initialize logger");
180        wasm_bindgen_futures::spawn_local(run(event_loop, window));
181    }
182}