From 2e6f79ea311323ba9e3903dfcb70b7021ac0c19e Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 2 Jan 2024 09:23:38 -0800 Subject: [PATCH] Moved ui rendering to pipeline --- crates/constants/src/lib.rs | 9 +- .../shaders/{sprite.wgsl => object.wgsl} | 0 crates/render/shaders/ui.wgsl | 72 +++++++++++ crates/render/src/gpustate.rs | 112 ++++++++++++------ crates/render/src/starfield.rs | 4 +- crates/ui/src/radar.rs | 1 - 6 files changed, 158 insertions(+), 40 deletions(-) rename crates/render/shaders/{sprite.wgsl => object.wgsl} (100%) create mode 100644 crates/render/shaders/ui.wgsl diff --git a/crates/constants/src/lib.rs b/crates/constants/src/lib.rs index 8275266..7d46ed5 100644 --- a/crates/constants/src/lib.rs +++ b/crates/constants/src/lib.rs @@ -44,8 +44,11 @@ pub const CONTENT_ROOT: &'static str = "./content"; /// Root directory of game textures pub const TEXTURE_ROOT: &'static str = "./assets/render"; -/// We can draw at most this many sprites on the screen. -pub const SPRITE_INSTANCE_LIMIT: u64 = 500; +/// We can draw at most this many object sprites on the screen. +pub const OBJECT_SPRITE_INSTANCE_LIMIT: u64 = 500; + +/// We can draw at most this many ui sprites on the screen. +pub const UI_SPRITE_INSTANCE_LIMIT: u64 = 100; /// Must be small enough to fit in an i32 -pub const STARFIELD_INSTANCE_LIMIT: u64 = STARFIELD_COUNT * 24; +pub const STARFIELD_SPRITE_INSTANCE_LIMIT: u64 = STARFIELD_COUNT * 24; diff --git a/crates/render/shaders/sprite.wgsl b/crates/render/shaders/object.wgsl similarity index 100% rename from crates/render/shaders/sprite.wgsl rename to crates/render/shaders/object.wgsl diff --git a/crates/render/shaders/ui.wgsl b/crates/render/shaders/ui.wgsl new file mode 100644 index 0000000..ea6de70 --- /dev/null +++ b/crates/render/shaders/ui.wgsl @@ -0,0 +1,72 @@ +struct InstanceInput { + @location(2) transform_matrix_0: vec4, + @location(3) transform_matrix_1: vec4, + @location(4) transform_matrix_2: vec4, + @location(5) transform_matrix_3: vec4, + @location(6) texture_idx: u32, +}; + +struct VertexInput { + @location(0) position: vec3, + @location(1) texture_coords: vec2, +} + +struct VertexOutput { + @builtin(position) position: vec4, + @location(0) texture_coords: vec2, + @location(1) index: u32, +} + + +@group(1) @binding(0) +var global: GlobalUniform; +struct GlobalUniform { + camera_position: vec2, + camera_zoom: vec2, + camera_zoom_limits: vec2, + window_size: vec2, + window_aspect: vec2, + starfield_texture: vec2, + starfield_tile_size: vec2, + starfield_size_limits: vec2, +}; + + +@group(0) @binding(0) +var texture_array: binding_array>; +@group(0) @binding(1) +var sampler_array: binding_array; + + + + +@vertex +fn vertex_main( + vertex: VertexInput, + instance: InstanceInput, +) -> VertexOutput { + + let transform = mat4x4( + instance.transform_matrix_0, + instance.transform_matrix_1, + instance.transform_matrix_2, + instance.transform_matrix_3, + ); + + var out: VertexOutput; + out.position = transform * vec4(vertex.position, 1.0); + out.texture_coords = vertex.texture_coords; + out.index = instance.texture_idx; + return out; +} + + +@fragment +fn fragment_main(in: VertexOutput) -> @location(0) vec4 { + return textureSampleLevel( + texture_array[in.index], + sampler_array[0], + in.texture_coords, + 0.0 + ).rgba; +} \ No newline at end of file diff --git a/crates/render/src/gpustate.rs b/crates/render/src/gpustate.rs index d4bc2aa..f65421d 100644 --- a/crates/render/src/gpustate.rs +++ b/crates/render/src/gpustate.rs @@ -37,8 +37,9 @@ pub struct GPUState { window_aspect: f32, - sprite_pipeline: wgpu::RenderPipeline, + object_pipeline: wgpu::RenderPipeline, starfield_pipeline: wgpu::RenderPipeline, + ui_pipeline: wgpu::RenderPipeline, starfield: Starfield, texture_array: TextureArray, @@ -47,8 +48,9 @@ pub struct GPUState { } struct VertexBuffers { - sprite: Rc, + object: Rc, starfield: Rc, + ui: Rc, } impl GPUState { @@ -117,12 +119,12 @@ impl GPUState { } let vertex_buffers = VertexBuffers { - sprite: Rc::new(VertexBuffer::new::( - "sprite", + object: Rc::new(VertexBuffer::new::( + "objecte", &device, Some(SPRITE_VERTICES), Some(SPRITE_INDICES), - galactica_constants::SPRITE_INSTANCE_LIMIT, + galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT, )), starfield: Rc::new(VertexBuffer::new::( @@ -130,7 +132,15 @@ impl GPUState { &device, Some(SPRITE_VERTICES), Some(SPRITE_INDICES), - galactica_constants::STARFIELD_INSTANCE_LIMIT, + galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT, + )), + + ui: Rc::new(VertexBuffer::new::( + "ui", + &device, + Some(SPRITE_VERTICES), + Some(SPRITE_INDICES), + galactica_constants::UI_SPRITE_INSTANCE_LIMIT, )), }; @@ -145,15 +155,15 @@ impl GPUState { ]; // Create render pipelines - let sprite_pipeline = PipelineBuilder::new("sprite", &device) + let object_pipeline = PipelineBuilder::new("object", &device) .set_shader(include_str!(concat!( env!("CARGO_MANIFEST_DIR"), "/shaders/", - "sprite.wgsl" + "object.wgsl" ))) .set_format(config.format) .set_triangle(true) - .set_vertex_buffer(&vertex_buffers.sprite) + .set_vertex_buffer(&vertex_buffers.object) .set_bind_group_layouts(bind_group_layouts) .build(); @@ -169,6 +179,18 @@ impl GPUState { .set_bind_group_layouts(bind_group_layouts) .build(); + let ui_pipeline = PipelineBuilder::new("ui", &device) + .set_shader(include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/shaders/", + "ui.wgsl" + ))) + .set_format(config.format) + .set_triangle(true) + .set_vertex_buffer(&vertex_buffers.ui) + .set_bind_group_layouts(bind_group_layouts) + .build(); + let mut starfield = Starfield::new(); starfield.regenerate(); @@ -182,8 +204,9 @@ impl GPUState { window_size, window_aspect, - sprite_pipeline, + object_pipeline, starfield_pipeline, + ui_pipeline, starfield, texture_array, @@ -404,14 +427,14 @@ impl GPUState { /// Will panic if SPRITE_INSTANCE_LIMIT is exceeded. /// /// This is only called inside self.render() - fn make_sprite_instances( + fn update_sprite_instances( &self, camera_zoom: f32, camera_pos: Point2, objects: &Vec, ui: &Vec, - ) -> Vec { - let mut instances: Vec = Vec::new(); + ) -> (usize, usize) { + let mut object_instances: Vec = Vec::new(); // Game coordinates (relative to camera) of ne and sw corners of screen. // Used to skip off-screen sprites. @@ -419,20 +442,45 @@ impl GPUState { let clip_sw = Point2::from((self.window_aspect, -1.0)) * camera_zoom; for s in objects { - self.push_object_sprite(camera_zoom, camera_pos, &mut instances, clip_ne, clip_sw, s); - } - - for s in ui { - self.push_ui_sprite(&mut instances, s); + self.push_object_sprite( + camera_zoom, + camera_pos, + &mut object_instances, + clip_ne, + clip_sw, + s, + ); } // Enforce sprite limit - if instances.len() as u64 > galactica_constants::SPRITE_INSTANCE_LIMIT { + if object_instances.len() as u64 > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT { // TODO: no panic, handle this better. panic!("Sprite limit exceeded!") } - return instances; + self.queue.write_buffer( + &self.vertex_buffers.object.instances, + 0, + bytemuck::cast_slice(&object_instances), + ); + + let mut ui_instances: Vec = Vec::new(); + + for s in ui { + self.push_ui_sprite(&mut ui_instances, s); + } + + if ui_instances.len() as u64 > galactica_constants::UI_SPRITE_INSTANCE_LIMIT { + panic!("Ui sprite limit exceeded!") + } + + self.queue.write_buffer( + &self.vertex_buffers.ui.instances, + 0, + bytemuck::cast_slice(&ui_instances), + ); + + return (object_instances.len(), ui_instances.len()); } /// Make a StarfieldInstance for each star that needs to be drawn. @@ -510,13 +558,8 @@ impl GPUState { ); // Create sprite instances - let sprite_instances = - self.make_sprite_instances(camera_zoom, camera_pos, object_sprites, ui_sprites); - self.queue.write_buffer( - &self.vertex_buffers.sprite.instances, - 0, - bytemuck::cast_slice(&sprite_instances), - ); + let (n_object, n_ui) = + self.update_sprite_instances(camera_zoom, camera_pos, object_sprites, ui_sprites); // These should match the indices in each shader, // and should each have a corresponding bind group layout. @@ -533,13 +576,14 @@ impl GPUState { ); // Sprite pipeline - self.vertex_buffers.sprite.set_in_pass(&mut render_pass); - render_pass.set_pipeline(&self.sprite_pipeline); - render_pass.draw_indexed( - 0..SPRITE_INDICES.len() as u32, - 0, - 0..sprite_instances.len() as _, - ); + self.vertex_buffers.object.set_in_pass(&mut render_pass); + render_pass.set_pipeline(&self.object_pipeline); + render_pass.draw_indexed(0..SPRITE_INDICES.len() as u32, 0, 0..n_object as _); + + // Ui pipeline + self.vertex_buffers.ui.set_in_pass(&mut render_pass); + render_pass.set_pipeline(&self.ui_pipeline); + render_pass.draw_indexed(0..SPRITE_INDICES.len() as u32, 0, 0..n_ui as _); // begin_render_pass borrows encoder mutably, so we can't call finish() // without dropping this variable. diff --git a/crates/render/src/starfield.rs b/crates/render/src/starfield.rs index 90348ae..a762fd4 100644 --- a/crates/render/src/starfield.rs +++ b/crates/render/src/starfield.rs @@ -94,7 +94,7 @@ impl Starfield { while ((nw_tile.x * 2 + 1) * (nw_tile.y * 2 + 1) * galactica_constants::STARFIELD_COUNT as i32) - > galactica_constants::STARFIELD_INSTANCE_LIMIT as i32 + > galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT as i32 { nw_tile -= Vector2::from((1, 1)); } @@ -119,7 +119,7 @@ impl Starfield { } // Enforce starfield limit - if instances.len() as u64 > galactica_constants::STARFIELD_INSTANCE_LIMIT { + if instances.len() as u64 > galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT { unreachable!("Starfield limit exceeded!") } self.instance_count = instances.len() as u32; diff --git a/crates/ui/src/radar.rs b/crates/ui/src/radar.rs index 7cb88e8..7e98660 100644 --- a/crates/ui/src/radar.rs +++ b/crates/ui/src/radar.rs @@ -33,7 +33,6 @@ pub fn build_radar( }; let m = d.magnitude() / radar_range; let d = d / radar_range * (radar_size / 2.0); - println!("{:?}", d); if m < 0.8 { let texture = ct.get_texture_handle("ui::radarframe"); let dimensions = Point2 {