From 861c1ce8e627ac91239892d5343d8ef97f37268f Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 8 Jan 2024 19:11:46 -0800 Subject: [PATCH] Minor cleanup --- crates/constants/src/lib.rs | 4 + crates/render/src/gpustate/hud.rs | 253 +++++++++++++++++++--------- crates/render/src/gpustate/mod.rs | 103 ++++++----- crates/render/src/gpustate/world.rs | 79 ++++++--- 4 files changed, 282 insertions(+), 157 deletions(-) diff --git a/crates/constants/src/lib.rs b/crates/constants/src/lib.rs index ece78af..4d75fd7 100644 --- a/crates/constants/src/lib.rs +++ b/crates/constants/src/lib.rs @@ -51,6 +51,10 @@ 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; +/// We can draw at most this many radual bars on the screen. +/// This is fairly small, since we know exactly how many of these we'll draw (for now) +pub const RADIALBAR_SPRITE_INSTANCE_LIMIT: u64 = 10; + /// The size of our circular particle buffer. When we create particles, the oldest ones are replaced. pub const PARTICLE_SPRITE_INSTANCE_LIMIT: u64 = 1000; diff --git a/crates/render/src/gpustate/hud.rs b/crates/render/src/gpustate/hud.rs index 00162ea..1bee4c2 100644 --- a/crates/render/src/gpustate/hud.rs +++ b/crates/render/src/gpustate/hud.rs @@ -12,7 +12,7 @@ use crate::{ }; impl GPUState { - pub(super) fn hud_add_radar(&mut self, state: &RenderState, instances: &mut Vec) { + pub(super) fn hud_add_radar(&mut self, state: &RenderState) { let radar_range = 4000.0; let radar_size = 300.0; let hide_range = 0.85; @@ -26,14 +26,26 @@ impl GPUState { let ship_sprite = state.content.get_sprite_handle("ui::shipblip"); let arrow_sprite = state.content.get_sprite_handle("ui::centerarrow"); - instances.push(UiInstance { - anchor: PositionAnchor::NwNw.to_int(), - position: [10.0, -10.0], - angle: 0.0, - size: radar_size, - color: [1.0, 1.0, 1.0, 1.0], - sprite_index: state.content.get_sprite_handle("ui::radar").get_index(), - }); + // Enforce buffer limit + if self.vertex_buffers.ui_counter as u64 > galactica_constants::UI_SPRITE_INSTANCE_LIMIT { + // TODO: no panic, handle this better. + panic!("UI limit exceeded!") + } + + // Push this object's instance + self.queue.write_buffer( + &self.vertex_buffers.ui.instances, + UiInstance::SIZE * self.vertex_buffers.ui_counter, + bytemuck::cast_slice(&[UiInstance { + anchor: PositionAnchor::NwNw.to_int(), + position: [10.0, -10.0], + angle: 0.0, + size: radar_size, + color: [1.0, 1.0, 1.0, 1.0], + sprite_index: state.content.get_sprite_handle("ui::radar").get_index(), + }]), + ); + self.vertex_buffers.ui_counter += 1; /* // Draw system objects @@ -95,14 +107,28 @@ impl GPUState { y: radar_size / -2.0 - 10.0, } + (d * (radar_size / 2.0)); - instances.push(UiInstance { - anchor: PositionAnchor::NwC.to_int(), - position: position.into(), - angle: -Rad::from(angle).0, // TODO: consistent angles - size, - color: f.into(), - sprite_index: ship_sprite.get_index(), - }); + // Enforce buffer limit + if self.vertex_buffers.ui_counter as u64 + > galactica_constants::UI_SPRITE_INSTANCE_LIMIT + { + // TODO: no panic, handle this better. + panic!("UI limit exceeded!") + } + + // Push this object's instance + self.queue.write_buffer( + &self.vertex_buffers.ui.instances, + UiInstance::SIZE * self.vertex_buffers.ui_counter, + bytemuck::cast_slice(&[UiInstance { + anchor: PositionAnchor::NwC.to_int(), + position: position.into(), + angle: -Rad::from(angle).0, // TODO: consistent angles + size, + color: f.into(), + sprite_index: ship_sprite.get_index(), + }]), + ); + self.vertex_buffers.ui_counter += 1; } } @@ -118,57 +144,85 @@ impl GPUState { let sprite = state.content.get_sprite_handle("ui::radarframe"); let size = 7.0f32.min((0.8 - m) * 70.0); - instances.push(UiInstance { - anchor: PositionAnchor::NwNw.to_int(), - position: Point2 { - x: (radar_size / 2.0 + 10.0) - d.x, - y: (radar_size / -2.0 - 10.0) + d.y, - } - .into(), - angle: 0.0, - size, - color, - sprite_index: sprite.get_index(), - }); + // Enforce buffer limit (this section adds four items) + if self.vertex_buffers.ui_counter as u64 + 4 + > galactica_constants::UI_SPRITE_INSTANCE_LIMIT + { + // TODO: no panic, handle this better. + panic!("UI limit exceeded!") + } - instances.push(UiInstance { - anchor: PositionAnchor::NwSw.to_int(), - position: Point2 { - x: (radar_size / 2.0 + 10.0) - d.x, - y: (radar_size / -2.0 - 10.0) - d.y, - } - .into(), - angle: Rad::from(Deg(90.0)).0, - size, - color, - sprite_index: sprite.get_index(), - }); + self.queue.write_buffer( + &self.vertex_buffers.ui.instances, + UiInstance::SIZE * self.vertex_buffers.ui_counter, + bytemuck::cast_slice(&[UiInstance { + anchor: PositionAnchor::NwNw.to_int(), + position: Point2 { + x: (radar_size / 2.0 + 10.0) - d.x, + y: (radar_size / -2.0 - 10.0) + d.y, + } + .into(), + angle: 0.0, + size, + color, + sprite_index: sprite.get_index(), + }]), + ); + self.vertex_buffers.ui_counter += 1; - instances.push(UiInstance { - anchor: PositionAnchor::NwSe.to_int(), - position: Point2 { - x: (radar_size / 2.0 + 10.0) + d.x, - y: (radar_size / -2.0 - 10.0) - d.y, - } - .into(), - angle: Rad::from(Deg(180.0)).0, - size, - color, - sprite_index: sprite.get_index(), - }); + self.queue.write_buffer( + &self.vertex_buffers.ui.instances, + UiInstance::SIZE * self.vertex_buffers.ui_counter, + bytemuck::cast_slice(&[UiInstance { + anchor: PositionAnchor::NwSw.to_int(), + position: Point2 { + x: (radar_size / 2.0 + 10.0) - d.x, + y: (radar_size / -2.0 - 10.0) - d.y, + } + .into(), + angle: Rad::from(Deg(90.0)).0, + size, + color, + sprite_index: sprite.get_index(), + }]), + ); + self.vertex_buffers.ui_counter += 1; - instances.push(UiInstance { - anchor: PositionAnchor::NwNe.to_int(), - position: Point2 { - x: (radar_size / 2.0 + 10.0) + d.x, - y: (radar_size / -2.0 - 10.0) + d.y, - } - .into(), - angle: Rad::from(Deg(270.0)).0, - size, - color, - sprite_index: sprite.get_index(), - }); + self.queue.write_buffer( + &self.vertex_buffers.ui.instances, + UiInstance::SIZE * self.vertex_buffers.ui_counter, + bytemuck::cast_slice(&[UiInstance { + anchor: PositionAnchor::NwSe.to_int(), + position: Point2 { + x: (radar_size / 2.0 + 10.0) + d.x, + y: (radar_size / -2.0 - 10.0) - d.y, + } + .into(), + angle: Rad::from(Deg(180.0)).0, + size, + color, + sprite_index: sprite.get_index(), + }]), + ); + self.vertex_buffers.ui_counter += 1; + + self.queue.write_buffer( + &self.vertex_buffers.ui.instances, + UiInstance::SIZE * self.vertex_buffers.ui_counter, + bytemuck::cast_slice(&[UiInstance { + anchor: PositionAnchor::NwNe.to_int(), + position: Point2 { + x: (radar_size / 2.0 + 10.0) + d.x, + y: (radar_size / -2.0 - 10.0) + d.y, + } + .into(), + angle: Rad::from(Deg(270.0)).0, + size, + color, + sprite_index: sprite.get_index(), + }]), + ); + self.vertex_buffers.ui_counter += 1; } // Arrow to center of system @@ -182,31 +236,60 @@ impl GPUState { y: radar_size / -2.0 - 10.0, } + ((q.normalize() * 0.865) * (radar_size / 2.0)); - instances.push(UiInstance { - anchor: PositionAnchor::NwC.to_int(), - position: position.into(), - angle: -player_angle.0, - size: 10.0, - color: [1.0, 1.0, 1.0, 1f32.min((m - 200.0) / 400.0)], - sprite_index: arrow_sprite.get_index(), - }); + if self.vertex_buffers.ui_counter as u64 > galactica_constants::UI_SPRITE_INSTANCE_LIMIT + { + // TODO: no panic, handle this better. + panic!("UI limit exceeded!") + } + + self.queue.write_buffer( + &self.vertex_buffers.ui.instances, + UiInstance::SIZE * self.vertex_buffers.ui_counter, + bytemuck::cast_slice(&[UiInstance { + anchor: PositionAnchor::NwC.to_int(), + position: position.into(), + angle: -player_angle.0, + size: 10.0, + color: [1.0, 1.0, 1.0, 1f32.min((m - 200.0) / 400.0)], + sprite_index: arrow_sprite.get_index(), + }]), + ); + self.vertex_buffers.ui_counter += 1; } } - pub(super) fn hud_add_status(&mut self, state: &RenderState, instances: &mut Vec) { - instances.push(UiInstance { - anchor: PositionAnchor::NeNe.to_int(), - position: [-10.0, -10.0], - angle: 0.0, - size: 200.0, - color: [1.0, 1.0, 1.0, 1.0], - sprite_index: state.content.get_sprite_handle("ui::status").get_index(), - }); + pub(super) fn hud_add_status(&mut self, state: &RenderState) { + if self.vertex_buffers.ui_counter as u64 > galactica_constants::UI_SPRITE_INSTANCE_LIMIT { + // TODO: no panic, handle this better. + panic!("UI limit exceeded!") + } + + self.queue.write_buffer( + &self.vertex_buffers.ui.instances, + UiInstance::SIZE * self.vertex_buffers.ui_counter, + bytemuck::cast_slice(&[UiInstance { + anchor: PositionAnchor::NeNe.to_int(), + position: [-10.0, -10.0], + angle: 0.0, + size: 200.0, + color: [1.0, 1.0, 1.0, 1.0], + sprite_index: state.content.get_sprite_handle("ui::status").get_index(), + }]), + ); + self.vertex_buffers.ui_counter += 1; + + // We add two items here, so +2 + if self.vertex_buffers.radialbar_counter as u64 + 2 + > galactica_constants::RADIALBAR_SPRITE_INSTANCE_LIMIT + { + // TODO: no panic, handle this better. + panic!("Radialbar limit exceeded!") + } // TODO: counters for each buffer, remove arrays self.queue.write_buffer( &self.vertex_buffers.radialbar.instances, - RadialBarInstance::SIZE * 0, + RadialBarInstance::SIZE * self.vertex_buffers.radialbar_counter, bytemuck::cast_slice(&[RadialBarInstance { position: [-19.0, -19.0], anchor: PositionAnchor::NeNe.to_int(), @@ -216,10 +299,11 @@ impl GPUState { angle: -state.current_time / 2.0, }]), ); + self.vertex_buffers.radialbar_counter += 1; self.queue.write_buffer( &self.vertex_buffers.radialbar.instances, - RadialBarInstance::SIZE * 1, + RadialBarInstance::SIZE * self.vertex_buffers.radialbar_counter, bytemuck::cast_slice(&[RadialBarInstance { position: [-27.0, -27.0], anchor: PositionAnchor::NeNe.to_int(), @@ -229,5 +313,6 @@ impl GPUState { angle: state.current_time / 5.0, }]), ); + self.vertex_buffers.radialbar_counter += 1; } } diff --git a/crates/render/src/gpustate/mod.rs b/crates/render/src/gpustate/mod.rs index 13114f9..e86feb4 100644 --- a/crates/render/src/gpustate/mod.rs +++ b/crates/render/src/gpustate/mod.rs @@ -5,7 +5,7 @@ use galactica_constants; use rand::seq::SliceRandom; use std::{iter, rc::Rc}; -use wgpu; +use wgpu::{self, BufferAddress}; use winit::{self, window::Window}; use crate::{ @@ -58,16 +58,17 @@ pub struct GPUState { } struct VertexBuffers { + // Keeps track of length of each buffer + pub object_counter: BufferAddress, + pub ui_counter: BufferAddress, + pub particle_counter: BufferAddress, + pub radialbar_counter: BufferAddress, + object: Rc, starfield: Rc, ui: Rc, - /// The index of the next particle slot we'll write to. - /// This must cycle to 0 whenever it exceeds the size - /// of the particle instance array. - particle_array_head: u64, particle: Rc, - radialbar: Rc, } @@ -171,6 +172,11 @@ impl GPUState { } let vertex_buffers = VertexBuffers { + object_counter: 0, + ui_counter: 0, + particle_counter: 0, + radialbar_counter: 0, + object: Rc::new(VertexBuffer::new::( "object", &device, @@ -195,7 +201,6 @@ impl GPUState { galactica_constants::UI_SPRITE_INSTANCE_LIMIT, )), - particle_array_head: 0, particle: Rc::new(VertexBuffer::new::( "particle", &device, @@ -345,55 +350,30 @@ impl GPUState { self.update_starfield_buffer() } - /// Make an instance for all the game's sprites - /// (Objects and UI) - /// This will Will panic if any X_SPRITE_INSTANCE_LIMIT is exceeded. - fn update_sprite_instances(&mut self, state: &RenderState) -> (usize, usize) { - let mut object_instances: Vec = Vec::new(); - + /// Entrypoint for all vertex buffer builders + pub(super) fn update_all_buffers(&mut self, state: &RenderState) { // Game coordinates (relative to camera) of ne and sw corners of screen. // Used to skip off-screen sprites. let clip_ne = Point2::from((-self.window_aspect, 1.0)) * state.camera_zoom; let clip_sw = Point2::from((self.window_aspect, -1.0)) * state.camera_zoom; - // TODO:sort. Order matters. + // TODO: sorting. We don't need to sort ships, but we do need to sort system objects by z-level + // (which we don't yet draw) + // that should probably be done in iter_system(). + + // Order matters, it determines what is drawn on top. + // The order inside ships and projectiles doesn't matter, + // but ships should always be under projectiles. for s in state.world.iter_ships() { - self.world_push_ship(state, (clip_ne, clip_sw), &s, &mut object_instances); + self.world_push_ship(state, (clip_ne, clip_sw), &s); } for p in state.world.iter_projectiles() { - self.world_push_projectile(state, (clip_ne, clip_sw), &p, &mut object_instances); + self.world_push_projectile(state, (clip_ne, clip_sw), &p); } - // Enforce sprite limit - if object_instances.len() as u64 > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT { - // TODO: no panic, handle this better. - panic!("Sprite limit exceeded!") - } - - self.queue.write_buffer( - &self.vertex_buffers.object.instances, - 0, - bytemuck::cast_slice(&object_instances), - ); - - // TODO: we don't need an array, just use a counter - let mut ui_instances: Vec = Vec::new(); - - self.hud_add_radar(state, &mut ui_instances); - self.hud_add_status(state, &mut ui_instances); - - 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()); + self.hud_add_radar(state); + self.hud_add_status(state); } /// Make a StarfieldInstance for each star that needs to be drawn. @@ -457,6 +437,11 @@ impl GPUState { timestamp_writes: None, }); + self.vertex_buffers.object_counter = 0; + self.vertex_buffers.ui_counter = 0; + self.vertex_buffers.radialbar_counter = 0; + // Don't reset particle counter, it's special + let s = state.content.get_starfield_handle(); // Update global values @@ -488,7 +473,7 @@ impl GPUState { for i in state.particles.iter() { self.queue.write_buffer( &self.vertex_buffers.particle.instances, - ParticleInstance::SIZE * self.vertex_buffers.particle_array_head, + ParticleInstance::SIZE * self.vertex_buffers.particle_counter, bytemuck::cast_slice(&[ParticleInstance { position: [i.pos.x, i.pos.y], velocity: i.velocity.into(), @@ -501,17 +486,17 @@ impl GPUState { fade: i.fade, }]), ); - self.vertex_buffers.particle_array_head += 1; - if self.vertex_buffers.particle_array_head + self.vertex_buffers.particle_counter += 1; + if self.vertex_buffers.particle_counter == galactica_constants::PARTICLE_SPRITE_INSTANCE_LIMIT { - self.vertex_buffers.particle_array_head = 0; + self.vertex_buffers.particle_counter = 0; } } state.particles.clear(); // Create sprite instances - let (n_object, n_ui) = self.update_sprite_instances(&state); + self.update_all_buffers(&state); // These should match the indices in each shader, // and should each have a corresponding bind group layout. @@ -530,7 +515,11 @@ impl GPUState { // Sprite pipeline 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 _); + render_pass.draw_indexed( + 0..SPRITE_INDICES.len() as u32, + 0, + 0..self.vertex_buffers.object_counter as _, + ); // Particle pipeline self.vertex_buffers.particle.set_in_pass(&mut render_pass); @@ -544,13 +533,21 @@ impl GPUState { // 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 _); + render_pass.draw_indexed( + 0..SPRITE_INDICES.len() as u32, + 0, + 0..self.vertex_buffers.ui_counter as _, + ); // Radial progress bars // TODO: do we need to do this every time? self.vertex_buffers.radialbar.set_in_pass(&mut render_pass); render_pass.set_pipeline(&self.radialbar_pipeline); - render_pass.draw_indexed(0..SPRITE_INDICES.len() as u32, 0, 0..2); + render_pass.draw_indexed( + 0..SPRITE_INDICES.len() as u32, + 0, + 0..self.vertex_buffers.radialbar_counter as _, + ); // begin_render_pass borrows encoder mutably, so we can't call finish() // without dropping this variable. diff --git a/crates/render/src/gpustate/world.rs b/crates/render/src/gpustate/world.rs index 7662bc8..f0e8039 100644 --- a/crates/render/src/gpustate/world.rs +++ b/crates/render/src/gpustate/world.rs @@ -8,17 +8,18 @@ use galactica_world::{ }; use crate::{ - globaluniform::ObjectData, vertexbuffer::types::ObjectInstance, GPUState, RenderState, + globaluniform::ObjectData, + vertexbuffer::{types::ObjectInstance, BufferObject}, + GPUState, RenderState, }; impl GPUState { pub(super) fn world_push_ship( - &self, + &mut self, state: &RenderState, // NE and SW corners of screen screen_clip: (Point2, Point2), s: &ShipWorldObject, - instances: &mut Vec, ) { let (_, r) = state.world.get_ship_body(s.physics_handle).unwrap(); let ship_pos = util::rigidbody_position(&r); @@ -47,7 +48,7 @@ impl GPUState { return; } - let idx = instances.len(); + let idx = self.vertex_buffers.object_counter; // Write this object's location data self.queue.write_buffer( &self.global_uniform.object_buffer, @@ -64,11 +65,24 @@ impl GPUState { }]), ); + // Enforce buffer limit + if self.vertex_buffers.object_counter as u64 + > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT + { + // TODO: no panic, handle this better. + panic!("Sprite limit exceeded!") + } + // Push this object's instance - instances.push(ObjectInstance { - sprite_index: ship_cnt.sprite.get_index(), - object_index: idx as u32, - }); + self.queue.write_buffer( + &self.vertex_buffers.object.instances, + ObjectInstance::SIZE * self.vertex_buffers.object_counter, + bytemuck::cast_slice(&[ObjectInstance { + sprite_index: ship_cnt.sprite.get_index(), + object_index: idx as u32, + }]), + ); + self.vertex_buffers.object_counter += 1; // Draw engine flares if necessary if s.controls.thrust { @@ -80,7 +94,7 @@ impl GPUState { self.queue.write_buffer( &self.global_uniform.object_buffer, - ObjectData::SIZE * instances.len() as u64, + ObjectData::SIZE * self.vertex_buffers.object_counter as u64, bytemuck::cast_slice(&[ObjectData { xpos: f.pos.x, ypos: f.pos.y - f.size / 2.0, @@ -93,21 +107,33 @@ impl GPUState { }]), ); - instances.push(ObjectInstance { - sprite_index: flare.get_index(), - object_index: instances.len() as u32, - }); + // Enforce buffer limit + if self.vertex_buffers.object_counter as u64 + > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT + { + // TODO: no panic, handle this better. + panic!("Sprite limit exceeded!") + } + + self.queue.write_buffer( + &self.vertex_buffers.object.instances, + ObjectInstance::SIZE * self.vertex_buffers.object_counter, + bytemuck::cast_slice(&[ObjectInstance { + sprite_index: flare.get_index(), + object_index: self.vertex_buffers.object_counter as u32, + }]), + ); + self.vertex_buffers.object_counter += 1; } } } pub(super) fn world_push_projectile( - &self, + &mut self, state: &RenderState, // NE and SW corners of screen screen_clip: (Point2, Point2), p: &ProjectileWorldObject, - instances: &mut Vec, ) { let r = state.world.get_rigid_body(p.rigid_body).unwrap(); let proj_pos = util::rigidbody_position(&r); @@ -136,7 +162,7 @@ impl GPUState { return; } - let idx = instances.len(); + let idx = self.vertex_buffers.object_counter; // Write this object's location data self.queue.write_buffer( &self.global_uniform.object_buffer, @@ -153,10 +179,23 @@ impl GPUState { }]), ); + // Enforce buffer limit + if self.vertex_buffers.object_counter as u64 + > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT + { + // TODO: no panic, handle this better. + panic!("Sprite limit exceeded!") + } + // Push this object's instance - instances.push(ObjectInstance { - sprite_index: proj_cnt.sprite.get_index(), - object_index: idx as u32, - }); + self.queue.write_buffer( + &self.vertex_buffers.object.instances, + ObjectInstance::SIZE * self.vertex_buffers.object_counter, + bytemuck::cast_slice(&[ObjectInstance { + sprite_index: proj_cnt.sprite.get_index(), + object_index: idx as u32, + }]), + ); + self.vertex_buffers.object_counter += 1; } }