diff --git a/README.md b/README.md index 8262781..0c24437 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,4 @@ I will try to keep this list up to date with all resources, which I use for learning. - https://sotrh.github.io/learn-wgpu/#what-is-wgpu (Starting point for this project) - https://github.com/sotrh/learn-wgpu (Matching GitHub Repo) +- https://www.youtube.com/watch?v=vV8mwo65kR8 (Building WebGPU with Rust Talk by FOSDEM) diff --git a/src/challenge_shader.wgsl b/src/challenge_shader.wgsl new file mode 100644 index 0000000..d1b9b86 --- /dev/null +++ b/src/challenge_shader.wgsl @@ -0,0 +1,27 @@ +// Vertex shader + +struct VertexOutput { + [[builtin(position)]] clip_coordinates: vec4; + [[location(0)]] position: vec2; +}; + +[[stage(vertex)]] +fn main( + [[builtin(vertex_index)]] in_vertex_index: u32, +) -> VertexOutput { + var out: VertexOutput; + let x = f32(1 - i32(in_vertex_index)) * 0.5; + let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5; + out.clip_coordinates = vec4(x, y, 0.0, 1.0); + out.position = vec2(x, y); + return out; +} + +// Fragment shader + +[[stage(fragment)]] +fn main(in: VertexOutput) -> [[location(0)]] vec4 { + let r = in.position.x; + let g = in.position.y; + return vec4(r, g, 0.1, 1.0); +} diff --git a/src/shader.wgsl b/src/shader.wgsl new file mode 100644 index 0000000..372ee89 --- /dev/null +++ b/src/shader.wgsl @@ -0,0 +1,23 @@ +// Vertex shader + +struct VertexOutput { + [[builtin(position)]] clip_position: vec4; +}; + +[[stage(vertex)]] +fn main( + [[builtin(vertex_index)]] in_vertex_index: u32, +) -> VertexOutput { + var out: VertexOutput; + let x = f32(1 - i32(in_vertex_index)) * 0.5; + let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5; + out.clip_position = vec4(x, y, 0.0, 1.0); + return out; +} + +// Fragment shader + +[[stage(fragment)]] +fn main(in: VertexOutput) -> [[location(0)]] vec4 { + return vec4(0.3, 0.2, 0.1, 1.0); +} diff --git a/src/state.rs b/src/state.rs index bd7d3d5..e910b4b 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,3 +1,4 @@ +use wgpu::PrimitiveTopology; use winit::{ event::*, window::Window, @@ -12,24 +13,32 @@ pub struct State { swap_chain: wgpu::SwapChain, pub size: winit::dpi::PhysicalSize, clear_color: wgpu::Color, + render_pipeline: wgpu::RenderPipeline, + challenge_render_pipeline: wgpu::RenderPipeline, + use_challenge_render_pipeline: bool, } impl State { pub async fn new(window: &Window) -> Self { // actual screen size let size = window.inner_size(); + // handle to gpu + // PRIMARY, VULKAN, DX12, METAL, BROWSER_WEBGPU let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); + // a surface to draw to let surface = unsafe { instance.create_surface(window) }; - // get a working adapter for the current system + + // get a physical adapter for the current system let adapter = instance.request_adapter( &wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::default(), compatible_surface: Some(&surface), }, ).await.unwrap(); - // adapter device and queue + + // logical device and command queue to work with let (device, queue) = adapter.request_device( &wgpu::DeviceDescriptor { // no special features required @@ -40,6 +49,7 @@ impl State { }, None, // Trace path ).await.unwrap(); + // description of the swap_chain let sc_desc = wgpu::SwapChainDescriptor { // how textures will be used @@ -52,9 +62,139 @@ impl State { // how to sync with the display present_mode: wgpu::PresentMode::Fifo, }; + // actually create a swap_chain let swap_chain = device.create_swap_chain(&surface, &sc_desc); + // load shader file + let shader = device.create_shader_module( + &wgpu::ShaderModuleDescriptor { + label: Some("Shader"), + flags: wgpu::ShaderFlags::all(), + source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), + } + ); + + // TODO + let render_pipeline_layout = device.create_pipeline_layout( + &wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layput"), + bind_group_layouts: &[], + push_constant_ranges: &[], + } + ); + + // add everything to the render_pipeline + let render_pipeline = device.create_render_pipeline( + &wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + // function name in shader.wgsl for [[stage(vertex)]] + entry_point: "main", + // already specified in the shader + buffers: &[], + + }, + // needed to sotre color data to swap_chain + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "main", + // setup of color outputs + targets: &[wgpu::ColorTargetState { + format: sc_desc.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrite::ALL, + }], + }), + primitive: wgpu::PrimitiveState { + topology: PrimitiveTopology::TriangleList, + strip_index_format: None, + // triangle facing forward when Counter Clock Wise + front_face: wgpu::FrontFace::Ccw, + cull_mode: Some(wgpu::Face::Back), + polygon_mode: wgpu::PolygonMode::Fill, + clamp_depth: false, + conservative: false, + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + // only use 1 sample => no extra sampling + count: 1, + // use all + mask: !0, + // not using anti aliasing + alpha_to_coverage_enabled: false, + } + } + ); + + // overwrite challenge shader file to shader + let shader = device.create_shader_module( + &wgpu::ShaderModuleDescriptor { + label: Some("Shader"), + flags: wgpu::ShaderFlags::all(), + source: wgpu::ShaderSource::Wgsl( + include_str!("challenge_shader.wgsl").into()), + } + ); + + // TODO + let render_pipeline_layout = device.create_pipeline_layout( + &wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layput"), + bind_group_layouts: &[], + push_constant_ranges: &[], + } + ); + + // add everything to the challnge_render_pipeline + let challenge_render_pipeline = device.create_render_pipeline( + &wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + // function name in shader.wgsl for [[stage(vertex)]] + entry_point: "main", + // already specified in the shader + buffers: &[], + + }, + // needed to sotre color data to swap_chain + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "main", + // setup of color outputs + targets: &[wgpu::ColorTargetState { + format: sc_desc.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrite::ALL, + }], + }), + primitive: wgpu::PrimitiveState { + topology: PrimitiveTopology::TriangleList, + strip_index_format: None, + // triangle facing forward when Counter Clock Wise + front_face: wgpu::FrontFace::Ccw, + cull_mode: Some(wgpu::Face::Back), + polygon_mode: wgpu::PolygonMode::Fill, + clamp_depth: false, + conservative: false, + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + // only use 1 sample => no extra sampling + count: 1, + // use all + mask: !0, + // not using anti aliasing + alpha_to_coverage_enabled: false, + } + } + ); + Self { surface, device, @@ -63,6 +203,9 @@ impl State { swap_chain, size, clear_color: wgpu::Color { r: 0.6, g: 0.6, b: 0.1, a: 1.0 }, + render_pipeline, + challenge_render_pipeline, + use_challenge_render_pipeline: false, } } @@ -80,10 +223,21 @@ impl State { pub fn input(&mut self, event: &WindowEvent) -> bool { match event { WindowEvent::CursorMoved { position, .. } => { - let x = (position.x as f64) / 1000 as f64; - let y = (position.y as f64) / 1000 as f64; + let x = (position.x as f64) / self.size.width as f64; + let y = (position.y as f64) / self.size.height as f64; self.clear_color = wgpu::Color { r: x, g: x, b: y, a: 1.0}; true + }, + WindowEvent::KeyboardInput { + input: KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Space), + .. + }, + .. + } => { + self.use_challenge_render_pipeline = !self.use_challenge_render_pipeline; + true } _ => false } @@ -106,7 +260,7 @@ impl State { label: Some("Render Encoder"), }); // create a render_pass - let _render_pass = encoder.begin_render_pass( + let mut render_pass = encoder.begin_render_pass( &wgpu::RenderPassDescriptor { label: Some("Render Pass"), color_attachments: &[ @@ -122,8 +276,17 @@ impl State { depth_stencil_attachment: None, }); + // set pipeline + if self.use_challenge_render_pipeline { + render_pass.set_pipeline(&self.challenge_render_pipeline); + } else { + render_pass.set_pipeline(&self.render_pipeline); + } + // draw triangle + render_pass.draw(0..3, 0..1); + // drop so encoder isn't borrowed mutually anymore - drop(_render_pass); + drop(render_pass); // submit finished command buffers self.queue.submit(std::iter::once(encoder.finish()));