From 59edbb10fea4e09bdbc88390036dd4c1e4505e63 Mon Sep 17 00:00:00 2001 From: marceline-cramer Date: Sun, 30 Jan 2022 22:51:24 -0700 Subject: [PATCH] Initial transformed mesh rendering --- src/main.rs | 98 ++++++++++++++++++++++++++++++++++++++++++++++--- src/shader.wgsl | 19 ++++++++-- 2 files changed, 108 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3da6b9e..b2b2cdc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use wgpu::util::DeviceExt; use winit::{ @@ -20,6 +21,8 @@ struct Renderer { camera_uniform: CameraUniform, camera_buffer: wgpu::Buffer, camera_bind_group: wgpu::BindGroup, + meshes_buffer: wgpu::Buffer, + meshes_bind_group: wgpu::BindGroup, render_pipeline: wgpu::RenderPipeline, } @@ -93,17 +96,47 @@ impl Renderer { label: Some("Camera Bind Group"), }); + let meshes_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Meshes Buffer"), + size: 65536, // TODO resizable meshes buffer/gpu vectors + usage: wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::UNIFORM + | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + let meshes_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + label: Some("Meshes Bind Group Layout"), + }); + + let meshes_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &camera_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: meshes_buffer.as_entire_binding(), + }], + label: Some("Meshes Bind Group"), + }); + let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Render Pipeline Layout"), - bind_group_layouts: &[&camera_bind_group_layout], + bind_group_layouts: &[&camera_bind_group_layout, &meshes_bind_group_layout], push_constant_ranges: &[], }); - let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { - label: Some("Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), - }); + let shader = device.create_shader_module(&wgpu::include_wgsl!("shader.wgsl")); let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Render Pipeline"), @@ -150,6 +183,8 @@ impl Renderer { camera_uniform, camera_buffer, camera_bind_group, + meshes_buffer, + meshes_bind_group, render_pipeline, } } @@ -168,6 +203,43 @@ impl Renderer { camera: &impl Camera, meshes: &MeshCommands, ) -> Result<(), wgpu::SurfaceError> { + self.camera_uniform.update(camera); + self.queue.write_buffer( + &self.camera_buffer, + 0, + bytemuck::cast_slice(&[self.camera_uniform]), + ); + + let mut sorted_meshes = HashMap::>::new(); + for mesh in meshes.iter() { + let group_id = mesh.handle.group_id; + if let Some(by_group) = sorted_meshes.get_mut(&group_id) { + by_group.push(*mesh); + } else { + let new_list = vec![*mesh]; + sorted_meshes.insert(group_id, new_list); + } + } + + let mut mesh_transforms = Vec::<[f32; 16]>::new(); + let mut transform_ranges = Vec::<(usize, std::ops::Range)>::new(); + + // this code assumes MeshHandle only uses group_id (which it does for now) + // TODO bucket by sub_id too before MeshHandle supports it + for (group_id, instances) in sorted_meshes.iter() { + let start_idx = mesh_transforms.len() as u32; + let transforms = instances.iter().map(|i| i.transform.to_cols_array()); + mesh_transforms.extend(transforms); + let end_idx = mesh_transforms.len() as u32; + transform_ranges.push((*group_id, start_idx..end_idx)); + } + + self.queue.write_buffer( + &self.meshes_buffer, + 0, + bytemuck::cast_slice(&mesh_transforms), + ); + let output = self.surface.get_current_texture()?; let view = output .texture @@ -179,7 +251,7 @@ impl Renderer { }); { - let mut _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("Render Pass"), color_attachments: &[wgpu::RenderPassColorAttachment { view: &view, @@ -196,6 +268,20 @@ impl Renderer { }], depth_stencil_attachment: None, }); + + render_pass.set_pipeline(&self.render_pipeline); + render_pass.set_bind_group(0, &self.camera_bind_group, &[]); + render_pass.set_bind_group(1, &self.meshes_bind_group, &[]); + + // TODO one group per mesh, still... + // TODO this could be implemented without accessing private members + for (group_id, meshes_range) in transform_ranges.iter() { + let group = self.mesh_pool.groups.get(*group_id).unwrap(); + render_pass.set_vertex_buffer(0, group.vertices.slice(..)); + render_pass.set_index_buffer(group.indices.slice(..), wgpu::IndexFormat::Uint32); + let indices = 0..(group.index_capacity as u32); + render_pass.draw_indexed(indices, 0, meshes_range.to_owned()); + } } self.queue.submit(std::iter::once(encoder.finish())); diff --git a/src/shader.wgsl b/src/shader.wgsl index 67d9726..6076a4f 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -2,8 +2,13 @@ struct CameraUniform { vp: mat4x4; }; -[[group(0), binding(0)]] -var camera: CameraUniform; +struct MeshInstance { + transform: mat4x4; +}; + +struct MeshData { + instances: array; +}; struct VertexInput { [[location(0)]] position: vec3; @@ -13,10 +18,18 @@ struct VertexOutput { [[builtin(position)]] clip_position: vec4; }; +[[group(0), binding(0)]] +var camera: CameraUniform; + +[[group(1), binding(0)]] +var meshes: MeshData; + [[stage(vertex)]] fn vs_main( - vertex: VertexInput + [[builtin(instance_index)]] mesh_idx: u32, + vertex: VertexInput, ) -> VertexOutput { + let transform = meshes.instances[mesh_idx].transform; var out: VertexOutput; out.clip_position = camera.vp * vec4(vertex.position, 1.0); return out;