diff options
Diffstat (limited to 'src/renderer')
| -rw-r--r-- | src/renderer/mod.rs | 248 |
1 files changed, 207 insertions, 41 deletions
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index ff503c8..2aabd48 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -13,6 +13,7 @@ use crate::{ sim::{ cell::materials::MaterialId, cell_sim::world::World, + particle_sim::{ParticleManager, particle::Particle}, rb_sim::{RbSimManager, debug_render::DebugVertex, rb_entity::RbEntity}, }, }; @@ -21,12 +22,13 @@ use crate::{ const CHUNK_SLOTS: usize = 11 * 200; const RB_ENTITY_SLOTS: usize = 64; const CELL_SLOTS: usize = CHUNK_SLOTS + RB_ENTITY_SLOTS; +const PARTICLE_SLOTS: usize = 10_000; const INITIAL_DEBUG_VERTEX_CAPACITY: usize = 4096; #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -struct Instance { +struct RendererInstance { centre: [f32; 2], cos_sin: [f32; 2], half_size: [f32; 2], @@ -35,6 +37,14 @@ struct Instance { _padding: u32, } +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct RendererParticle { + position: [f32; 2], + data: u32, + _padding: u32, +} + fn srgb_to_linear(channel: u8) -> f32 { let channel = channel as f32 / 255.0; if channel <= 0.04045 { @@ -70,12 +80,19 @@ pub struct RendererState { pub egui_state: egui_winit::State, egui_renderer: egui_wgpu::Renderer, - // world pixels - pixels_pipeline: wgpu::RenderPipeline, + // common camera_uniform_buffer: wgpu::Buffer, + + // world pixels + instance_pipeline: wgpu::RenderPipeline, instance_buffer: wgpu::Buffer, cell_buffer: wgpu::Buffer, - pixels_bind_group: wgpu::BindGroup, + instance_bind_group: wgpu::BindGroup, + + // particles + particle_pipeline: wgpu::RenderPipeline, + particle_buffer: wgpu::Buffer, + particle_bind_group: wgpu::BindGroup, // physics debug lines debug_pipeline: wgpu::RenderPipeline, @@ -157,7 +174,26 @@ impl RendererState { } }); - let pixels_bind_group_layout = + // --- PALETTE --- + let mut palette = [0.0f32; MaterialId::ALL.len() * 4]; + for material in MaterialId::ALL { + let def = material.def(); + let i = material as usize * 4; + palette[i] = srgb_to_linear(def.color.0); + palette[i + 1] = srgb_to_linear(def.color.1); + palette[i + 2] = srgb_to_linear(def.color.2); + palette[i + 3] = def.color.3 as f32 / 255.0; + } + + let palette_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Palette buffer"), + size: size_of_val(&palette) as u64, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + // --- INSTANCES --- + let instance_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, entries: &[ @@ -208,29 +244,29 @@ impl RendererState { ], }); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("../shader/shader.wgsl").into()), + let instance_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Instance shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("../shader/instance.wgsl").into()), }); - let pixels_pipeline_layout = + let instance_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, immediate_size: 0, - bind_group_layouts: &[Some(&pixels_bind_group_layout)], + bind_group_layouts: &[Some(&instance_bind_group_layout)], }); - let pixels_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + let instance_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: None, - layout: Some(&pixels_pipeline_layout), + layout: Some(&instance_pipeline_layout), vertex: wgpu::VertexState { - module: &shader, + module: &instance_shader, entry_point: Some("vs_main"), buffers: &[], compilation_options: wgpu::PipelineCompilationOptions::default(), }, fragment: Some(wgpu::FragmentState { - module: &shader, + module: &instance_shader, entry_point: Some("fs_main"), targets: &[Some(wgpu::ColorTargetState { format: config.format, @@ -261,28 +297,11 @@ impl RendererState { mapped_at_creation: false, }); - let mut palette = [0.0f32; MaterialId::ALL.len() * 4]; - for material in MaterialId::ALL { - let def = material.def(); - let i = material as usize * 4; - palette[i] = srgb_to_linear(def.color.0); - palette[i + 1] = srgb_to_linear(def.color.1); - palette[i + 2] = srgb_to_linear(def.color.2); - palette[i + 3] = def.color.3 as f32 / 255.0; - } - - let palette_buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Palette buffer"), - size: size_of_val(&palette) as u64, - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - queue.write_buffer(&palette_buffer, 0, bytemuck::cast_slice(&palette)); let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some("Instance buffer"), - size: (CELL_SLOTS * size_of::<Instance>()) as u64, + size: (CELL_SLOTS * size_of::<RendererInstance>()) as u64, usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); @@ -294,9 +313,9 @@ impl RendererState { mapped_at_creation: false, }); - let pixels_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + let instance_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, - layout: &pixels_bind_group_layout, + layout: &instance_bind_group_layout, entries: &[ wgpu::BindGroupEntry { binding: 0, @@ -317,6 +336,120 @@ impl RendererState { ], }); + // --- PARTICLES --- + let particle_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: None, + entries: &[ + // camera uniform + wgpu::BindGroupLayoutEntry { + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + binding: 0, + count: None, + visibility: wgpu::ShaderStages::VERTEX, + }, + // palette + wgpu::BindGroupLayoutEntry { + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + binding: 1, + count: None, + visibility: wgpu::ShaderStages::FRAGMENT, + }, + // instance buffer + wgpu::BindGroupLayoutEntry { + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + binding: 2, + count: None, + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, + }, + ], + }); + + let particle_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Particle shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("../shader/particle.wgsl").into()), + }); + + let particle_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + immediate_size: 0, + bind_group_layouts: &[Some(&particle_bind_group_layout)], + }); + + let particle_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&particle_pipeline_layout), + vertex: wgpu::VertexState { + module: &particle_shader, + entry_point: Some("vs_main"), + buffers: &[], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &particle_shader, + entry_point: Some("fs_main"), + targets: &[Some(wgpu::ColorTargetState { + format: config.format, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleStrip, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview_mask: None, + cache: None, + }); + + let particle_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Particle buffer"), + size: (PARTICLE_SLOTS * size_of::<RendererParticle>()) as u64, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + let particle_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &particle_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: camera_uniform_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: palette_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: particle_buffer.as_entire_binding(), + }, + ], + }); + + // --- DEBUG --- let debug_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("Debug bind group layout"), @@ -409,11 +542,16 @@ impl RendererState { egui_state, egui_renderer, - pixels_pipeline, camera_uniform_buffer, + + instance_pipeline, instance_buffer, cell_buffer, - pixels_bind_group, + instance_bind_group, + + particle_pipeline, + particle_buffer, + particle_bind_group, debug_pipeline, debug_bind_group, @@ -437,6 +575,7 @@ impl RendererState { &mut self, world: &mut World, rb_sim_manager: &mut RbSimManager, + particle_manager: &mut ParticleManager, camera: &mut Camera, config: &mut Config, diagnostics: &Diagnostics, @@ -528,7 +667,7 @@ impl RendererState { ); let mut cell_buffer: [u8; CELLS_IN_CHUNK] = [0; CELLS_IN_CHUNK]; - let mut instances: Vec<Instance> = Vec::new(); + let mut instances: Vec<RendererInstance> = Vec::new(); { puffin::profile_scope!("Upload chunk cells"); @@ -587,7 +726,7 @@ impl RendererState { for cx in cxl..=cxu { for cy in cyl..=cyu { if let Some(idx) = world.chunk_position_to_chunk_idx.get(&(cx, cy)) { - instances.push(Instance { + instances.push(RendererInstance { centre: [ (cx * CHUNK_SIZE) as f32 + half_size[0], (cy * CHUNK_SIZE) as f32 + half_size[1], @@ -606,7 +745,7 @@ impl RendererState { if let Some(&RbEntity { width, height, .. }) = rb_sim_manager.rb_entities.get(id) && let Some((x, y, cos, sin)) = rb_sim_manager.get_rb_entity_transform(*id) { - instances.push(Instance { + instances.push(RendererInstance { centre: [x, y], cos_sin: [cos, sin], half_size: [width as f32 / 2.0, height as f32 / 2.0], @@ -621,6 +760,25 @@ impl RendererState { .write_buffer(&self.instance_buffer, 0, bytemuck::cast_slice(&instances)); } + let mut particles: Vec<RendererParticle> = Vec::new(); + + { + puffin::profile_scope!("Build particles"); + // TODO could cull this to visible, maybe it's slower? + particles = particle_manager + .particles + .iter() + .map(|p| RendererParticle { + position: [p.position.x, p.position.y], + data: p.material as u32, + _padding: 0, + }) + .collect(); + + self.queue + .write_buffer(&self.particle_buffer, 0, bytemuck::cast_slice(&particles)); + } + let debug_vertex_count = if config.debug_render { puffin::profile_scope!("Build physics debug lines"); let vertices = rb_sim_manager.debug_render(config.debug_render_mode); @@ -671,6 +829,7 @@ impl RendererState { multiview_mask: None, }); + // this is kind of a hack let instances_start = u32::min( if config.cells_render { 0 @@ -680,10 +839,17 @@ impl RendererState { instances.len() as u32 - 1, ); - render_pass.set_pipeline(&self.pixels_pipeline); - render_pass.set_bind_group(0, &self.pixels_bind_group, &[]); + // instances + render_pass.set_pipeline(&self.instance_pipeline); + render_pass.set_bind_group(0, &self.instance_bind_group, &[]); render_pass.draw(0..4, instances_start..instances.len() as u32); + // particles + render_pass.set_pipeline(&self.particle_pipeline); + render_pass.set_bind_group(0, &self.particle_bind_group, &[]); + render_pass.draw(0..4, 0..particles.len() as u32); + + // debug if debug_vertex_count > 0 { render_pass.set_pipeline(&self.debug_pipeline); render_pass.set_bind_group(0, &self.debug_bind_group, &[]); |
