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            experimental_features: wgpu::ExperimentalFeatures::disabled(),
35            memory_hints: wgpu::MemoryHints::MemoryUsage,
36            trace: wgpu::Trace::Off,
37        })
38        .await
39        .expect("Failed to create device");
40
41    // Load the shaders from disk
42    let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
43        label: None,
44        source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
45    });
46
47    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
48        label: None,
49        bind_group_layouts: &[],
50        push_constant_ranges: &[],
51    });
52
53    let swapchain_capabilities = surface.get_capabilities(&adapter);
54    let swapchain_format = swapchain_capabilities.formats[0];
55
56    let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
57        label: None,
58        layout: Some(&pipeline_layout),
59        vertex: wgpu::VertexState {
60            module: &shader,
61            entry_point: Some("vs_main"),
62            buffers: &[],
63            compilation_options: Default::default(),
64        },
65        fragment: Some(wgpu::FragmentState {
66            module: &shader,
67            entry_point: Some("fs_main"),
68            compilation_options: Default::default(),
69            targets: &[Some(swapchain_format.into())],
70        }),
71        primitive: wgpu::PrimitiveState::default(),
72        depth_stencil: None,
73        multisample: wgpu::MultisampleState::default(),
74        multiview: None,
75        cache: None,
76    });
77
78    let mut config = surface
79        .get_default_config(&adapter, size.width, size.height)
80        .unwrap();
81    surface.configure(&device, &config);
82
83    let window = &window;
84    event_loop
85        .run(move |event, target| {
86            // Have the closure take ownership of the resources.
87            // `event_loop.run` never returns, therefore we must do this to ensure
88            // the resources are properly cleaned up.
89            let _ = (&instance, &adapter, &shader, &pipeline_layout);
90
91            if let Event::WindowEvent {
92                window_id: _,
93                event,
94            } = event
95            {
96                match event {
97                    WindowEvent::Resized(new_size) => {
98                        // Reconfigure the surface with the new size
99                        config.width = new_size.width.max(1);
100                        config.height = new_size.height.max(1);
101                        surface.configure(&device, &config);
102                        // On macos the window needs to be redrawn manually after resizing
103                        window.request_redraw();
104                    }
105                    WindowEvent::RedrawRequested => {
106                        let frame = surface
107                            .get_current_texture()
108                            .expect("Failed to acquire next swap chain 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 mut rpass =
118                                encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
119                                    label: None,
120                                    color_attachments: &[Some(wgpu::RenderPassColorAttachment {
121                                        view: &view,
122                                        depth_slice: None,
123                                        resolve_target: None,
124                                        ops: wgpu::Operations {
125                                            load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
126                                            store: wgpu::StoreOp::Store,
127                                        },
128                                    })],
129                                    depth_stencil_attachment: None,
130                                    timestamp_writes: None,
131                                    occlusion_query_set: None,
132                                });
133                            rpass.set_pipeline(&render_pipeline);
134                            rpass.draw(0..3, 0..1);
135                        }
136
137                        queue.submit(Some(encoder.finish()));
138                        window.pre_present_notify();
139                        frame.present();
140                    }
141                    WindowEvent::CloseRequested => target.exit(),
142                    _ => {}
143                };
144            }
145        })
146        .unwrap();
147}
148
149pub fn main() {
150    let event_loop = EventLoop::new().unwrap();
151    #[cfg_attr(
152        not(target_arch = "wasm32"),
153        expect(unused_mut, reason = "`wasm32` re-assigns to specify canvas")
154    )]
155    let mut builder = winit::window::WindowBuilder::new();
156    #[cfg(target_arch = "wasm32")]
157    {
158        use wasm_bindgen::JsCast;
159        use winit::platform::web::WindowBuilderExtWebSys;
160        let canvas = web_sys::window()
161            .unwrap()
162            .document()
163            .unwrap()
164            .get_element_by_id("canvas")
165            .unwrap()
166            .dyn_into::<web_sys::HtmlCanvasElement>()
167            .unwrap();
168        builder = builder.with_canvas(Some(canvas));
169    }
170    let window = builder.build(&event_loop).unwrap();
171
172    #[cfg(not(target_arch = "wasm32"))]
173    {
174        env_logger::init();
175        pollster::block_on(run(event_loop, window));
176    }
177    #[cfg(target_arch = "wasm32")]
178    {
179        std::panic::set_hook(Box::new(console_error_panic_hook::hook));
180        console_log::init().expect("could not initialize logger");
181        wasm_bindgen_futures::spawn_local(run(event_loop, window));
182    }
183}