From f177cc716c5f2aa5b50b14ccbb421de89e3a7854 Mon Sep 17 00:00:00 2001 From: Kai Stevenson Date: Sun, 16 Aug 2026 17:29:42 -0700 Subject: wip --- src/renderer/mod.rs | 256 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 189 insertions(+), 67 deletions(-) (limited to 'src/renderer/mod.rs') diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 5231bc3..e022fe7 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -2,6 +2,7 @@ mod ui; use std::sync::Arc; +use fxhash::FxHashMap; use winit::window::Window; use crate::{ @@ -9,7 +10,7 @@ use crate::{ camera::Camera, config::{CELLS_IN_CHUNK, CHUNK_SIZE}, renderer::ui::draw_egui, - sim::world::World, + sim::{cell_sim::world::World, rb_sim::RbSimManager}, }; struct RendererChunk { @@ -17,6 +18,11 @@ struct RendererChunk { bind_group: wgpu::BindGroup, } +struct RendererRbEntity { + texture: wgpu::Texture, + bind_group: wgpu::BindGroup, +} + #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] struct ChunkData { @@ -42,6 +48,8 @@ pub struct RendererState { pixels_pipeline: wgpu::RenderPipeline, camera_uniform_buffer: wgpu::Buffer, camera_uniform_bind_group: wgpu::BindGroup, + + renderer_rb_entities: FxHashMap, } impl RendererState { @@ -270,6 +278,8 @@ impl RendererState { pixels_pipeline, camera_uniform_bind_group, camera_uniform_buffer, + + renderer_rb_entities: FxHashMap::default(), } } @@ -285,6 +295,7 @@ impl RendererState { pub fn render( &mut self, world: &mut World, + rb_sim_manager: &RbSimManager, camera: &mut Camera, config: &mut Config, diagnostics: &Diagnostics, @@ -420,81 +431,192 @@ impl RendererState { } { - puffin::profile_scope!("Main render pass"); - let mut render_pass: wgpu::RenderPass<'_> = - encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - depth_slice: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.0, - g: 0.0, - b: 0.0, - a: 1.0, - }), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - occlusion_query_set: None, - timestamp_writes: None, - multiview_mask: None, - }); + puffin::profile_scope!("Upload entity textures"); + for entity in rb_sim_manager.rb_entities.values() { + // TODO add "needs texture update"? - render_pass.set_pipeline(&self.pixels_pipeline); + // TODO use material palette to improve bandwidth of upload + for i in 0..CELLS_IN_CHUNK { + let material = entity.cells[i].material.def(); + chunk_buffer[i * 4] = material.color.0; + chunk_buffer[i * 4 + 1] = material.color.1; + chunk_buffer[i * 4 + 2] = material.color.2; + chunk_buffer[i * 4 + 3] = material.color.3; + } - render_pass.set_bind_group(0, &self.camera_uniform_bind_group, &[]); + let render_entity = &self.renderer_rb_entities.entry(entity.id).or_insert({ + let texture = self.device.create_texture(&wgpu::TextureDescriptor { + label: None, + mip_level_count: 1, + sample_count: 1, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + format: wgpu_types::TextureFormat::Rgba8UnormSrgb, + size: wgpu::Extent3d { + width: CHUNK_SIZE as u32, + height: CHUNK_SIZE as u32, + depth_or_array_layers: 1, + }, + dimension: wgpu::TextureDimension::D2, + view_formats: &[], + }); + + let bind_group_layout = + self.device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: None, + entries: &[wgpu::BindGroupLayoutEntry { + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { + filterable: true, + }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + binding: 0, + count: None, + visibility: wgpu::ShaderStages::FRAGMENT, + }], + }); + + let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&texture.create_view( + &wgpu::TextureViewDescriptor { + dimension: Some(wgpu::TextureViewDimension::D2), + usage: Some( + wgpu::TextureUsages::TEXTURE_BINDING + | wgpu::TextureUsages::COPY_DST, + ), + ..wgpu::TextureViewDescriptor::default() + }, + )), + }], + }); + + RendererRbEntity { + texture, + bind_group, + } + }); - let (xl, xu, yl, yu) = camera.viewport_bounds_world(); - let ((cxl, cyl), _) = World::split_game_position(xl.floor() as i32, yl.floor() as i32); - let ((cxu, cyu), _) = World::split_game_position(xu.floor() as i32, yu.floor() as i32); + self.queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: &render_entity.texture, + aspect: wgpu::TextureAspect::All, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + }, + &chunk_buffer, + wgpu::TexelCopyBufferLayout { + bytes_per_row: Some(CHUNK_SIZE as u32 * 4), + offset: 0, + rows_per_image: Some(CHUNK_SIZE as u32), + }, + wgpu::Extent3d { + width: CHUNK_SIZE as u32, + height: CHUNK_SIZE as u32, + depth_or_array_layers: 1, + }, + ); + } - // TODO only visible chunks - for cx in cxl..=cxu { - for cy in cyl..=cyu { - if let Some(idx) = world.chunk_position_to_chunk_idx.get(&(cx, cy)) { - let render_chunk = &self.renderer_chunks[*idx]; - render_pass.set_bind_group(1, &render_chunk.bind_group, &[]); - render_pass - .set_immediates(0, bytemuck::bytes_of(&ChunkData { origin: [cx, cy] })); - render_pass.draw(0..4, 0..1); + { + puffin::profile_scope!("Main render pass"); + let mut render_pass: wgpu::RenderPass<'_> = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + depth_slice: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 1.0, + }), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + occlusion_query_set: None, + timestamp_writes: None, + multiview_mask: None, + }); + + render_pass.set_pipeline(&self.pixels_pipeline); + + render_pass.set_bind_group(0, &self.camera_uniform_bind_group, &[]); + + let (xl, xu, yl, yu) = camera.viewport_bounds_world(); + let ((cxl, cyl), _) = + World::split_game_position(xl.floor() as i32, yl.floor() as i32); + let ((cxu, cyu), _) = + World::split_game_position(xu.floor() as i32, yu.floor() as i32); + + for cx in cxl..=cxu { + for cy in cyl..=cyu { + if let Some(idx) = world.chunk_position_to_chunk_idx.get(&(cx, cy)) { + let render_chunk = &self.renderer_chunks[*idx]; + render_pass.set_bind_group(1, &render_chunk.bind_group, &[]); + render_pass.set_immediates( + 0, + bytemuck::bytes_of(&ChunkData { origin: [cx, cy] }), + ); + render_pass.draw(0..4, 0..1); + } } } - } - } - { - puffin::profile_scope!("Egui render pass"); - let mut egui_pass = encoder - .begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("egui pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - depth_slice: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - multiview_mask: None, - }) - .forget_lifetime(); + // TODO this is inefficient + for rb_entity in rb_sim_manager.rb_entities.keys() { + let renderer_rb_entity = self.renderer_rb_entities.get(rb_entity).unwrap(); + let (x, y) = rb_sim_manager.get_rb_entity_position(*rb_entity).unwrap(); + render_pass.set_bind_group(1, &renderer_rb_entity.bind_group, &[]); + render_pass.set_immediates( + 0, + bytemuck::bytes_of(&ChunkData { + origin: [x.round() as i32, y.round() as i32], + }), + ); + render_pass.draw(0..4, 0..1); + } + } - self.egui_renderer - .render(&mut egui_pass, &clipped_primitives, &screen_descriptor); - } + { + puffin::profile_scope!("Egui render pass"); + let mut egui_pass = encoder + .begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("egui pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + depth_slice: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + multiview_mask: None, + }) + .forget_lifetime(); + + self.egui_renderer + .render(&mut egui_pass, &clipped_primitives, &screen_descriptor); + } - { - puffin::profile_scope!("Submit queue and present"); - self.queue.submit(std::iter::once(encoder.finish())); - output.present(); + { + puffin::profile_scope!("Submit queue and present"); + self.queue.submit(std::iter::once(encoder.finish())); + output.present(); + } } } } -- cgit v1.3.1