wgpu_examples/uniform_values/
mod.rs
1use std::sync::Arc;
20use encase::ShaderType;
23use winit::{
24 event::{Event, KeyEvent, WindowEvent},
25 event_loop::EventLoop,
26 keyboard::{Key, NamedKey},
27 window::Window,
28};
29
30const ZOOM_INCREMENT_FACTOR: f32 = 1.1;
31const CAMERA_POS_INCREMENT_FACTOR: f32 = 0.1;
32
33#[derive(Debug, ShaderType)]
35struct AppState {
36 pub cursor_pos: glam::Vec2,
37 pub zoom: f32,
38 pub max_iterations: u32,
39}
40
41impl AppState {
42 fn as_wgsl_bytes(&self) -> encase::internal::Result<Vec<u8>> {
62 let mut buffer = encase::UniformBuffer::new(Vec::new());
63 buffer.write(self)?;
64 Ok(buffer.into_inner())
65 }
66
67 fn translate_view(&mut self, increments: i32, axis: usize) {
68 self.cursor_pos[axis] += CAMERA_POS_INCREMENT_FACTOR * increments as f32 / self.zoom;
69 }
70
71 fn zoom(&mut self, amount: f32) {
72 self.zoom += ZOOM_INCREMENT_FACTOR * amount * self.zoom.powf(1.02);
73 self.zoom = self.zoom.max(1.1);
74 }
75}
76
77impl Default for AppState {
78 fn default() -> Self {
79 AppState {
80 cursor_pos: glam::Vec2::ZERO,
81 zoom: 1.0,
82 max_iterations: 50,
83 }
84 }
85}
86
87struct WgpuContext {
88 pub window: Arc<Window>,
89 pub surface: wgpu::Surface<'static>,
90 pub surface_config: wgpu::SurfaceConfiguration,
91 pub device: wgpu::Device,
92 pub queue: wgpu::Queue,
93 pub pipeline: wgpu::RenderPipeline,
94 pub bind_group: wgpu::BindGroup,
95 pub uniform_buffer: wgpu::Buffer,
96}
97
98impl WgpuContext {
99 async fn new(window: Arc<Window>) -> WgpuContext {
100 let size = window.inner_size();
101
102 let instance = wgpu::Instance::default();
103 let surface = instance.create_surface(window.clone()).unwrap();
104 let adapter = instance
105 .request_adapter(&wgpu::RequestAdapterOptions {
106 power_preference: wgpu::PowerPreference::HighPerformance,
107 compatible_surface: Some(&surface),
108 force_fallback_adapter: false,
109 })
110 .await
111 .unwrap();
112 let (device, queue) = adapter
113 .request_device(&wgpu::DeviceDescriptor {
114 label: None,
115 required_features: wgpu::Features::empty(),
116 required_limits: wgpu::Limits::downlevel_defaults(),
117 memory_hints: wgpu::MemoryHints::MemoryUsage,
118 trace: wgpu::Trace::Off,
119 })
120 .await
121 .unwrap();
122
123 let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
124
125 let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
127 label: None,
128 size: size_of::<AppState>() as u64,
129 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
130 mapped_at_creation: false,
131 });
132
133 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
135 label: None,
136 entries: &[wgpu::BindGroupLayoutEntry {
137 binding: 0,
138 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
139 ty: wgpu::BindingType::Buffer {
140 ty: wgpu::BufferBindingType::Uniform,
141 has_dynamic_offset: false,
142 min_binding_size: None,
143 },
144 count: None,
145 }],
146 });
147 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
148 label: None,
149 layout: &bind_group_layout,
150 entries: &[wgpu::BindGroupEntry {
151 binding: 0,
152 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
153 buffer: &uniform_buffer,
154 offset: 0,
155 size: None,
156 }),
157 }],
158 });
159
160 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
161 label: None,
162 bind_group_layouts: &[&bind_group_layout],
164 push_constant_ranges: &[],
165 });
166
167 let swapchain_capabilities = surface.get_capabilities(&adapter);
168 let swapchain_format = swapchain_capabilities.formats[0];
169
170 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
171 label: None,
172 layout: Some(&pipeline_layout),
173 vertex: wgpu::VertexState {
174 module: &shader,
175 entry_point: Some("vs_main"),
176 compilation_options: Default::default(),
177 buffers: &[],
178 },
179 fragment: Some(wgpu::FragmentState {
180 module: &shader,
181 entry_point: Some("fs_main"),
182 compilation_options: Default::default(),
183 targets: &[Some(swapchain_format.into())],
184 }),
185 primitive: wgpu::PrimitiveState::default(),
186 depth_stencil: None,
187 multisample: wgpu::MultisampleState::default(),
188 multiview: None,
189 cache: None,
190 });
191 let surface_config = surface
192 .get_default_config(&adapter, size.width, size.height)
193 .unwrap();
194 surface.configure(&device, &surface_config);
195
196 WgpuContext {
198 window,
199 surface,
200 surface_config,
201 device,
202 queue,
203 pipeline,
204 bind_group,
205 uniform_buffer,
206 }
207 }
208
209 fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
210 self.surface_config.width = new_size.width;
211 self.surface_config.height = new_size.height;
212 self.surface.configure(&self.device, &self.surface_config);
213 self.window.request_redraw();
214 }
215}
216
217async fn run(event_loop: EventLoop<()>, window: Arc<Window>) {
218 let mut wgpu_context = Some(WgpuContext::new(window.clone()).await);
219 let mut state = Some(AppState::default());
221 let main_window_id = wgpu_context.as_ref().unwrap().window.id();
222 event_loop
223 .run(move |event, target| {
224 match event {
225 Event::LoopExiting => {
226 wgpu_context = None;
227 state = None;
228 }
229 Event::WindowEvent { window_id, event } if window_id == main_window_id => {
230 match event {
231 WindowEvent::CloseRequested => {
232 target.exit();
233 }
234 WindowEvent::KeyboardInput {
235 event:
236 KeyEvent {
237 logical_key, text, ..
238 },
239 ..
240 } => {
241 let state_mut = state.as_mut().unwrap();
242 let wgpu_context_ref = wgpu_context.as_ref().unwrap();
243
244 if let Key::Named(key) = logical_key {
245 match key {
246 NamedKey::Escape => target.exit(),
247 NamedKey::ArrowUp => state_mut.translate_view(1, 1),
248 NamedKey::ArrowDown => state_mut.translate_view(-1, 1),
249 NamedKey::ArrowLeft => state_mut.translate_view(-1, 0),
250 NamedKey::ArrowRight => state_mut.translate_view(1, 0),
251 _ => {}
252 }
253 }
254
255 if let Some(text) = text {
256 if text == "u" {
257 state_mut.max_iterations += 3;
258 } else if text == "d" {
259 state_mut.max_iterations -= 3;
260 }
261 };
262
263 wgpu_context_ref.window.request_redraw();
264 }
265 WindowEvent::MouseWheel { delta, .. } => {
266 let change = match delta {
267 winit::event::MouseScrollDelta::LineDelta(_, vertical) => vertical,
268 winit::event::MouseScrollDelta::PixelDelta(pos) => {
269 pos.y as f32 / 20.0
270 }
271 };
272 let state_mut = state.as_mut().unwrap();
273 let wgpu_context_ref = wgpu_context.as_ref().unwrap();
274 state_mut.zoom(change);
276 wgpu_context_ref.window.request_redraw();
277 }
278 WindowEvent::Resized(new_size) => {
279 let wgpu_context_mut = wgpu_context.as_mut().unwrap();
280 wgpu_context_mut.resize(new_size);
281 wgpu_context_mut.window.request_redraw();
282 }
283 WindowEvent::RedrawRequested => {
284 let wgpu_context_ref = wgpu_context.as_ref().unwrap();
285 let state_ref = state.as_ref().unwrap();
286 let frame = wgpu_context_ref.surface.get_current_texture().unwrap();
287 let view = frame
288 .texture
289 .create_view(&wgpu::TextureViewDescriptor::default());
290
291 wgpu_context_ref.queue.write_buffer(
293 &wgpu_context_ref.uniform_buffer,
294 0,
295 &state_ref.as_wgsl_bytes().expect(
296 "Error in encase translating AppState \
297 struct to WGSL bytes.",
298 ),
299 );
300 let mut encoder = wgpu_context_ref.device.create_command_encoder(
301 &wgpu::CommandEncoderDescriptor { label: None },
302 );
303 {
304 let mut render_pass =
305 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
306 label: None,
307 color_attachments: &[Some(
308 wgpu::RenderPassColorAttachment {
309 view: &view,
310 depth_slice: None,
311 resolve_target: None,
312 ops: wgpu::Operations {
313 load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
314 store: wgpu::StoreOp::Store,
315 },
316 },
317 )],
318 depth_stencil_attachment: None,
319 occlusion_query_set: None,
320 timestamp_writes: None,
321 });
322 render_pass.set_pipeline(&wgpu_context_ref.pipeline);
323 render_pass.set_bind_group(
325 0,
326 Some(&wgpu_context_ref.bind_group),
327 &[],
328 );
329 render_pass.draw(0..3, 0..1);
330 }
331 wgpu_context_ref.queue.submit(Some(encoder.finish()));
332 window.pre_present_notify();
333 frame.present();
334 }
335 _ => {}
336 }
337 }
338 _ => {}
339 }
340 })
341 .unwrap();
342}
343
344pub fn main() {
345 let event_loop = EventLoop::new().unwrap();
346 #[cfg_attr(
347 not(target_arch = "wasm32"),
348 expect(unused_mut, reason = "`wasm32` re-assigns to specify canvas")
349 )]
350 let mut builder = winit::window::WindowBuilder::new()
351 .with_title("Remember: Use U/D to change sample count!")
352 .with_inner_size(winit::dpi::LogicalSize::new(900, 900));
353
354 #[cfg(target_arch = "wasm32")]
355 {
356 use wasm_bindgen::JsCast;
357 use winit::platform::web::WindowBuilderExtWebSys;
358 let canvas = web_sys::window()
359 .unwrap()
360 .document()
361 .unwrap()
362 .get_element_by_id("canvas")
363 .unwrap()
364 .dyn_into::<web_sys::HtmlCanvasElement>()
365 .unwrap();
366 builder = builder.with_canvas(Some(canvas));
367 }
368 let window = builder.build(&event_loop).unwrap();
369
370 let window = Arc::new(window);
371 #[cfg(not(target_arch = "wasm32"))]
372 {
373 env_logger::builder().format_timestamp_nanos().init();
374 pollster::block_on(run(event_loop, window));
375 }
376 #[cfg(target_arch = "wasm32")]
377 {
378 std::panic::set_hook(Box::new(console_error_panic_hook::hook));
379 console_log::init().expect("could not initialize logger");
380
381 let document = web_sys::window()
382 .and_then(|win| win.document())
383 .expect("Failed to get document.");
384 let body = document.body().unwrap();
385 let controls_text = document
386 .create_element("p")
387 .expect("Failed to create controls text as element.");
388 controls_text.set_inner_html(
389 "Controls: <br/>
390Up, Down, Left, Right: Move view, <br/>
391Scroll: Zoom, <br/>
392U, D: Increase / decrease sample count.",
393 );
394 body.append_child(&controls_text)
395 .expect("Failed to append controls text to body.");
396
397 wasm_bindgen_futures::spawn_local(run(event_loop, window));
398 }
399}