diff --git a/Cargo.toml b/Cargo.toml index da64b5d..edd2ec0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ env_logger = "0.10" log = "0.4" wgpu = "0.18" winit = "0.28" +bytemuck = { version = "1.12", features = [ "derive" ] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio = { version = "1.36.0", features = ["full"] } diff --git a/src/lib.rs b/src/lib.rs index 6aa472b..dea9c9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +use wgpu::util::DeviceExt; use winit::{ dpi::LogicalSize, event::*, @@ -22,6 +23,7 @@ pub async fn run() { let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_title("Renderer") + .with_min_inner_size(LogicalSize::new(180, 120)) .with_inner_size(LogicalSize::new(1280, 720)) .build(&event_loop) .unwrap(); @@ -103,6 +105,9 @@ pub struct State { // it gets dropped after it as the surface contains // unsafe references to the window's resources. window: Window, + render_pipeline: wgpu::RenderPipeline, + vertex_buffer: wgpu::Buffer, + num_vertices: u32, } impl State { @@ -168,13 +173,73 @@ impl State { format: surface_format, width: size.width, height: size.height, - present_mode: surface_caps.present_modes[0], + present_mode: wgpu::PresentMode::AutoNoVsync, //surface_caps.present_modes[0], alpha_mode: surface_caps.alpha_modes[0], view_formats: vec![], }; surface.configure(&device, &config); + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), + }); + + let render_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(VERTICES), + usage: wgpu::BufferUsages::VERTEX, + }); + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[Vertex::desc()], + }, + fragment: Some(wgpu::FragmentState { + // 3. + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + // 4. + format: config.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, // 1. + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, // 2. + cull_mode: Some(wgpu::Face::Back), + // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE + polygon_mode: wgpu::PolygonMode::Fill, + // Requires Features::DEPTH_CLIP_CONTROL + unclipped_depth: false, + // Requires Features::CONSERVATIVE_RASTERIZATION + conservative: false, + }, + depth_stencil: None, // 1. + multisample: wgpu::MultisampleState { + count: 1, // 2. + mask: !0, // 3. + alpha_to_coverage_enabled: false, // 4. + }, + multiview: None, // 5. + }); + + let num_vertices = VERTICES.len() as u32; + Self { window, surface, @@ -182,6 +247,9 @@ impl State { queue, config, size, + render_pipeline, + vertex_buffer, + num_vertices, } } @@ -218,16 +286,16 @@ impl State { }); { - let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("Render Pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, + r: 0.0, + g: 0.0, + b: 0.0, a: 1.0, }), store: wgpu::StoreOp::Store, @@ -237,6 +305,10 @@ impl State { occlusion_query_set: None, timestamp_writes: None, }); + + render_pass.set_pipeline(&self.render_pipeline); // 2. + render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); + render_pass.draw(0..3, 0..1); } // submit will accept anything that implements IntoIter @@ -246,3 +318,46 @@ impl State { Ok(()) } } + +#[repr(C)] +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +struct Vertex { + position: [f32; 3], + color: [f32; 3], +} + +impl Vertex { + fn desc() -> wgpu::VertexBufferLayout<'static> { + wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float32x3, + }, + ], + } + } +} + +const VERTICES: &[Vertex] = &[ + Vertex { + position: [0.0, 0.5, 0.0], + color: [1.0, 0.0, 0.0], + }, + Vertex { + position: [-0.5, -0.5, 0.0], + color: [0.0, 1.0, 0.0], + }, + Vertex { + position: [0.5, -0.5, 0.0], + color: [0.0, 0.0, 1.0], + }, +]; diff --git a/src/shader.wgsl b/src/shader.wgsl new file mode 100644 index 0000000..cc394ed --- /dev/null +++ b/src/shader.wgsl @@ -0,0 +1,28 @@ +// Vertex shader + +struct VertexInput { + @location(0) position: vec3, + @location(1) color: vec3, +}; + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) color: vec3, +}; + +@vertex +fn vs_main( + model: VertexInput, +) -> VertexOutput { + var out: VertexOutput; + out.color = model.color; + out.clip_position = vec4(model.position, 1.0); + return out; +} + +// Fragment shader + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4 { + return vec4(in.color, 1.0); +}