From 3561baa99c710b38de81bb1bb25e374c69bcf00c Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 17 Jan 2024 10:17:18 -0800 Subject: [PATCH] Minor reorganization --- crates/galactica/src/game.rs | 2 +- crates/render/shaders/include/anchor.wgsl | 6 + crates/render/src/datastructs.rs | 78 ------ crates/render/src/gpustate/mod.rs | 4 +- crates/render/src/gpustate/new.rs | 82 +----- crates/render/src/gpustate/phys.rs | 92 ++----- crates/render/src/gpustate/render.rs | 66 ++--- crates/render/src/lib.rs | 12 +- ...{anchoredposition.rs => positionanchor.rs} | 10 + crates/render/src/renderinput.rs | 36 +++ crates/render/src/renderstate.rs | 237 ++++++++++++++++++ crates/render/src/starfield.rs | 32 +-- crates/render/src/ui/fpsindicator.rs | 2 +- 13 files changed, 369 insertions(+), 290 deletions(-) delete mode 100644 crates/render/src/datastructs.rs rename crates/render/src/{anchoredposition.rs => positionanchor.rs} (83%) create mode 100644 crates/render/src/renderinput.rs create mode 100644 crates/render/src/renderstate.rs diff --git a/crates/galactica/src/game.rs b/crates/galactica/src/game.rs index e3e9d33..ece9f40 100644 --- a/crates/galactica/src/game.rs +++ b/crates/galactica/src/game.rs @@ -44,7 +44,7 @@ impl<'a> Game { ShipHandle { index: 0 }, FactionHandle { index: 0 }, ShipPersonality::Player, - Point2::new(0.0, 0.0), + Point2::new(0.0, 4000.0), ); let s = self.state.systemsim.get_ship_mut(&player).unwrap(); diff --git a/crates/render/shaders/include/anchor.wgsl b/crates/render/shaders/include/anchor.wgsl index 2c8b4f7..4c45cbd 100644 --- a/crates/render/shaders/include/anchor.wgsl +++ b/crates/render/shaders/include/anchor.wgsl @@ -28,6 +28,12 @@ fn anchor( } else if anchor == 5u { // NE NE trans += vec2(window_dim.x, window_dim.y) / 2.0; trans += vec2(-dim.x, -dim.y) / 2.0; + } else if anchor == 6u { // C C + trans += vec2(0.0, 0.0) / 2.0; + trans += vec2(0.0, 0.0) / 2.0; + } else if anchor == 7u { // C NW + trans += vec2(0.0, 0.0) / 2.0; + trans += vec2(dim.x, -dim.y) / 2.0; } else { // center / center as default, since it's the most visible variant. trans += vec2(0.0, 0.0) / 2.0; trans += vec2(0.0, 0.0) / 2.0; diff --git a/crates/render/src/datastructs.rs b/crates/render/src/datastructs.rs deleted file mode 100644 index 8318d1e..0000000 --- a/crates/render/src/datastructs.rs +++ /dev/null @@ -1,78 +0,0 @@ -use galactica_content::{Content, SystemHandle}; -use galactica_playeragent::PlayerAgent; -use galactica_system::phys::{ParticleBuilder, PhysSim}; -use galactica_util::timing::Timing; -use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer}; -use nalgebra::Vector2; -use std::rc::Rc; -use wgpu::BufferAddress; -use winit::window::Window; - -use crate::{globaluniform::GlobalUniform, vertexbuffer::VertexBuffer}; - -/// Bundles parameters passed to a single call to GPUState::render -pub struct RenderInput<'a> { - /// Camera position, in world units - pub camera_pos: Vector2, - - /// Player ship data - pub player: &'a PlayerAgent, - - /// The system we're currently in - pub current_system: SystemHandle, - - /// Height of screen, in world units - pub camera_zoom: f32, - - /// The world state to render - pub systemsim: &'a PhysSim, - - // TODO: handle overflow. is it a problem? - /// The current time, in seconds - pub current_time: f32, - - /// Game content - pub ct: &'a Content, - - /// Particles to spawn during this frame - pub particles: &'a Vec, - - /// Time we spent in each part of the game loop - pub timing: Timing, -} - -/// Renderer state. A reference to this struct is often passed to helper functions. -pub(crate) struct RenderState { - pub window: Window, - pub window_size: winit::dpi::PhysicalSize, - pub window_aspect: f32, - - pub queue: wgpu::Queue, - pub global_uniform: GlobalUniform, - pub vertex_buffers: VertexBuffers, - - pub text_font_system: FontSystem, - pub text_cache: SwashCache, - pub text_atlas: TextAtlas, - pub text_renderer: TextRenderer, -} - -/// Vertex buffers -pub(crate) struct VertexBuffers { - // Keeps track of length of each buffer - // Most of these are reset on each frame. - // - // The exception is particle_counter, which - // is never reset, and loops back to zero once - // it exceeds buffer length - pub object_counter: BufferAddress, - pub ui_counter: BufferAddress, - pub particle_counter: BufferAddress, - pub radialbar_counter: BufferAddress, - - pub object: Rc, - pub starfield: Rc, - pub ui: Rc, - pub particle: Rc, - pub radialbar: Rc, -} diff --git a/crates/render/src/gpustate/mod.rs b/crates/render/src/gpustate/mod.rs index 230423a..7284b2a 100644 --- a/crates/render/src/gpustate/mod.rs +++ b/crates/render/src/gpustate/mod.rs @@ -3,9 +3,7 @@ use galactica_content::Content; use wgpu; use winit; -use crate::{ - datastructs::RenderState, starfield::Starfield, texturearray::TextureArray, ui::UiManager, -}; +use crate::{starfield::Starfield, texturearray::TextureArray, ui::UiManager, RenderState}; /// GPUState is very big, so its methods have been split /// among the following files. diff --git a/crates/render/src/gpustate/new.rs b/crates/render/src/gpustate/new.rs index ee20deb..4351594 100644 --- a/crates/render/src/gpustate/new.rs +++ b/crates/render/src/gpustate/new.rs @@ -1,28 +1,11 @@ use anyhow::Result; use galactica_content::Content; -use galactica_util::constants::{ - OBJECT_SPRITE_INSTANCE_LIMIT, PARTICLE_SPRITE_INSTANCE_LIMIT, UI_SPRITE_INSTANCE_LIMIT, -}; use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer}; -use std::rc::Rc; use crate::{ - datastructs::{RenderState, VertexBuffers}, - globaluniform::GlobalUniform, - pipeline::PipelineBuilder, - shaderprocessor::preprocess_shader, - starfield::Starfield, - texturearray::TextureArray, - ui::UiManager, - vertexbuffer::{ - consts::{SPRITE_INDICES, SPRITE_VERTICES}, - types::{ - ObjectInstance, ParticleInstance, RadialBarInstance, StarfieldInstance, TexturedVertex, - UiInstance, - }, - VertexBuffer, - }, - GPUState, + globaluniform::GlobalUniform, pipeline::PipelineBuilder, shaderprocessor::preprocess_shader, + starfield::Starfield, texturearray::TextureArray, ui::UiManager, GPUState, RenderState, + VertexBuffers, }; impl GPUState { @@ -90,52 +73,7 @@ impl GPUState { surface.configure(&device, &config); } - let vertex_buffers = VertexBuffers { - object_counter: 0, - ui_counter: 0, - particle_counter: 0, - radialbar_counter: 0, - - object: Rc::new(VertexBuffer::new::( - "object", - &device, - Some(SPRITE_VERTICES), - Some(SPRITE_INDICES), - OBJECT_SPRITE_INSTANCE_LIMIT, - )), - - starfield: Rc::new(VertexBuffer::new::( - "starfield", - &device, - Some(SPRITE_VERTICES), - Some(SPRITE_INDICES), - ct.get_config().starfield_instance_limit, - )), - - ui: Rc::new(VertexBuffer::new::( - "ui", - &device, - Some(SPRITE_VERTICES), - Some(SPRITE_INDICES), - UI_SPRITE_INSTANCE_LIMIT, - )), - - particle: Rc::new(VertexBuffer::new::( - "particle", - &device, - Some(SPRITE_VERTICES), - Some(SPRITE_INDICES), - PARTICLE_SPRITE_INSTANCE_LIMIT, - )), - - radialbar: Rc::new(VertexBuffer::new::( - "radial bar", - &device, - Some(SPRITE_VERTICES), - Some(SPRITE_INDICES), - 10, - )), - }; + let vertex_buffers = VertexBuffers::new(&device, ct); // Load uniforms let global_uniform = GlobalUniform::new(&device); @@ -197,7 +135,7 @@ impl GPUState { )) .set_format(config.format) .set_triangle(true) - .set_vertex_buffer(&vertex_buffers.object) + .set_vertex_buffer(vertex_buffers.get_object()) .set_bind_group_layouts(bind_group_layouts) .build(); @@ -213,7 +151,7 @@ impl GPUState { )) .set_format(config.format) .set_triangle(true) - .set_vertex_buffer(&vertex_buffers.starfield) + .set_vertex_buffer(vertex_buffers.get_starfield()) .set_bind_group_layouts(bind_group_layouts) .build(); @@ -225,7 +163,7 @@ impl GPUState { )) .set_format(config.format) .set_triangle(true) - .set_vertex_buffer(&vertex_buffers.ui) + .set_vertex_buffer(vertex_buffers.get_ui()) .set_bind_group_layouts(bind_group_layouts) .build(); @@ -241,7 +179,7 @@ impl GPUState { )) .set_format(config.format) .set_triangle(true) - .set_vertex_buffer(&vertex_buffers.particle) + .set_vertex_buffer(vertex_buffers.get_particle()) .set_bind_group_layouts(bind_group_layouts) .build(); @@ -257,7 +195,7 @@ impl GPUState { )) .set_format(config.format) .set_triangle(true) - .set_vertex_buffer(&vertex_buffers.radialbar) + .set_vertex_buffer(vertex_buffers.get_radialbar()) .set_bind_group_layouts(bind_group_layouts) .build(); @@ -281,7 +219,7 @@ impl GPUState { }; return Ok(Self { - ui: UiManager::new(&mut state), + ui: UiManager::new(ct, &mut state), device, config, surface, diff --git a/crates/render/src/gpustate/phys.rs b/crates/render/src/gpustate/phys.rs index 68a1697..dd9b3af 100644 --- a/crates/render/src/gpustate/phys.rs +++ b/crates/render/src/gpustate/phys.rs @@ -2,13 +2,11 @@ use bytemuck; use galactica_system::data::ShipState; -use galactica_util::{constants::OBJECT_SPRITE_INSTANCE_LIMIT, to_radians}; +use galactica_util::to_radians; use nalgebra::{Point2, Point3}; use crate::{ - globaluniform::ObjectData, - vertexbuffer::{types::ObjectInstance, BufferObject}, - GPUState, RenderInput, + globaluniform::ObjectData, vertexbuffer::types::ObjectInstance, GPUState, RenderInput, }; impl GPUState { @@ -68,7 +66,7 @@ impl GPUState { continue; } - let idx = self.state.vertex_buffers.object_counter; + let idx = self.state.get_object_counter(); // Write this object's location data self.state.queue.write_buffer( &self.state.global_uniform.object_buffer, @@ -85,22 +83,11 @@ impl GPUState { }]), ); - // Enforce buffer limit - if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT { - // TODO: no panic, handle this better. - panic!("Sprite limit exceeded!") - } - // Push this object's instance - self.state.queue.write_buffer( - &self.state.vertex_buffers.object.instances, - ObjectInstance::SIZE * self.state.vertex_buffers.object_counter, - bytemuck::cast_slice(&[ObjectInstance { - sprite_index: ship_cnt.sprite.get_index(), - object_index: idx as u32, - }]), - ); - self.state.vertex_buffers.object_counter += 1; + self.state.push_object_buffer(ObjectInstance { + sprite_index: ship_cnt.sprite.get_index(), + object_index: idx as u32, + }); let flare = ship.data.get_outfits().get_flare_sprite(state.ct); if { @@ -115,7 +102,7 @@ impl GPUState { for engine_point in &ship_cnt.engines { self.state.queue.write_buffer( &self.state.global_uniform.object_buffer, - ObjectData::SIZE * self.state.vertex_buffers.object_counter as u64, + ObjectData::SIZE * self.state.get_object_counter() as u64, bytemuck::cast_slice(&[ObjectData { // Note that we adjust the y-coordinate for half-height, // not the x-coordinate, even though our ships point east @@ -135,23 +122,10 @@ impl GPUState { }]), ); - // Enforce buffer limit - if self.state.vertex_buffers.object_counter as u64 - > OBJECT_SPRITE_INSTANCE_LIMIT - { - // TODO: no panic, handle this better. - panic!("Sprite limit exceeded!") - } - - self.state.queue.write_buffer( - &self.state.vertex_buffers.object.instances, - ObjectInstance::SIZE * self.state.vertex_buffers.object_counter, - bytemuck::cast_slice(&[ObjectInstance { - sprite_index: flare.unwrap().get_index(), - object_index: self.state.vertex_buffers.object_counter as u32, - }]), - ); - self.state.vertex_buffers.object_counter += 1; + self.state.push_object_buffer(ObjectInstance { + sprite_index: flare.unwrap().get_index(), + object_index: self.state.get_object_counter() as u32, + }); } } } @@ -191,7 +165,7 @@ impl GPUState { continue; } - let idx = self.state.vertex_buffers.object_counter; + let idx = self.state.get_object_counter(); // Write this object's location data self.state.queue.write_buffer( &self.state.global_uniform.object_buffer, @@ -208,22 +182,11 @@ impl GPUState { }]), ); - // Enforce buffer limit - if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT { - // TODO: no panic, handle this better. - panic!("Sprite limit exceeded!") - } - // Push this object's instance - self.state.queue.write_buffer( - &self.state.vertex_buffers.object.instances, - ObjectInstance::SIZE * self.state.vertex_buffers.object_counter, - bytemuck::cast_slice(&[ObjectInstance { - sprite_index: proj_cnt.sprite.get_index(), - object_index: idx as u32, - }]), - ); - self.state.vertex_buffers.object_counter += 1; + self.state.push_object_buffer(ObjectInstance { + sprite_index: proj_cnt.sprite.get_index(), + object_index: idx as u32, + }); } } @@ -255,7 +218,7 @@ impl GPUState { continue; } - let idx = self.state.vertex_buffers.object_counter; + let idx = self.state.get_object_counter(); // Write this object's location data self.state.queue.write_buffer( &self.state.global_uniform.object_buffer, @@ -272,22 +235,11 @@ impl GPUState { }]), ); - // Enforce buffer limit - if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT { - // TODO: no panic, handle this better. - panic!("Sprite limit exceeded!") - } - // Push this object's instance - self.state.queue.write_buffer( - &self.state.vertex_buffers.object.instances, - ObjectInstance::SIZE * self.state.vertex_buffers.object_counter, - bytemuck::cast_slice(&[ObjectInstance { - sprite_index: o.sprite.get_index(), - object_index: idx as u32, - }]), - ); - self.state.vertex_buffers.object_counter += 1; + self.state.push_object_buffer(ObjectInstance { + sprite_index: o.sprite.get_index(), + object_index: idx as u32, + }); } } } diff --git a/crates/render/src/gpustate/render.rs b/crates/render/src/gpustate/render.rs index e3d0c41..e34b1b5 100644 --- a/crates/render/src/gpustate/render.rs +++ b/crates/render/src/gpustate/render.rs @@ -8,7 +8,7 @@ use wgpu; use crate::{ globaluniform::GlobalDataContent, - vertexbuffer::{consts::SPRITE_INDICES, types::ParticleInstance, BufferObject}, + vertexbuffer::{consts::SPRITE_INDICES, types::ParticleInstance}, RenderInput, }; @@ -45,11 +45,7 @@ impl super::GPUState { timestamp_writes: None, }); - self.state.vertex_buffers.object_counter = 0; - self.state.vertex_buffers.ui_counter = 0; - self.state.vertex_buffers.radialbar_counter = 0; - // Don't reset particle counter, it's special - + self.state.frame_reset(); let s = input.ct.get_starfield_handle(); // Update global values @@ -81,25 +77,17 @@ impl super::GPUState { // Write all new particles to GPU buffer for i in input.particles.iter() { - self.state.queue.write_buffer( - &self.state.vertex_buffers.particle.instances, - ParticleInstance::SIZE * self.state.vertex_buffers.particle_counter, - bytemuck::cast_slice(&[ParticleInstance { - position: [i.pos.x, i.pos.y], - velocity: i.velocity.into(), - angle: i.angle, - angvel: i.angvel, - size: i.size, - sprite_index: i.sprite.get_index(), - created: input.current_time, - expires: input.current_time + i.lifetime, - fade: i.fade, - }]), - ); - self.state.vertex_buffers.particle_counter += 1; - if self.state.vertex_buffers.particle_counter == PARTICLE_SPRITE_INSTANCE_LIMIT { - self.state.vertex_buffers.particle_counter = 0; - } + self.state.push_particle_buffer(ParticleInstance { + position: [i.pos.x, i.pos.y], + velocity: i.velocity.into(), + angle: i.angle, + angvel: i.angvel, + size: i.size, + sprite_index: i.sprite.get_index(), + created: input.current_time, + expires: input.current_time + i.lifetime, + fade: i.fade, + }); } // Create sprite instances @@ -109,10 +97,6 @@ impl super::GPUState { let clip_ne = Point2::new(-self.state.window_aspect, 1.0) * input.camera_zoom; let clip_sw = Point2::new(self.state.window_aspect, -1.0) * input.camera_zoom; - // 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. @@ -129,31 +113,31 @@ impl super::GPUState { // Starfield pipeline self.state .vertex_buffers - .starfield + .get_starfield() .set_in_pass(&mut render_pass); render_pass.set_pipeline(&self.starfield_pipeline); render_pass.draw_indexed( 0..SPRITE_INDICES.len() as u32, 0, - 0..self.starfield.instance_count, + 0..self.state.get_starfield_counter(), ); // Sprite pipeline self.state .vertex_buffers - .object + .get_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..self.state.vertex_buffers.object_counter as _, + 0..self.state.get_object_counter(), ); // Particle pipeline self.state .vertex_buffers - .particle + .get_particle() .set_in_pass(&mut render_pass); render_pass.set_pipeline(&self.particle_pipeline); render_pass.draw_indexed( @@ -163,27 +147,31 @@ impl super::GPUState { ); // Ui pipeline - self.state.vertex_buffers.ui.set_in_pass(&mut render_pass); + self.state + .vertex_buffers + .get_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..self.state.vertex_buffers.ui_counter as _, + 0..self.state.get_ui_counter(), ); // Radial progress bars // TODO: do we need to do this every time? self.state .vertex_buffers - .radialbar + .get_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..self.state.vertex_buffers.radialbar_counter as _, + 0..self.state.get_radialbar_counter(), ); + let textareas = self.ui.get_textareas(&input, &self.state); self.state .text_renderer .prepare( @@ -195,7 +183,7 @@ impl super::GPUState { width: self.state.window_size.width, height: self.state.window_size.height, }, - self.ui.get_textareas(), + textareas, &mut self.state.text_cache, ) .unwrap(); diff --git a/crates/render/src/lib.rs b/crates/render/src/lib.rs index 9cfe681..60a5ad3 100644 --- a/crates/render/src/lib.rs +++ b/crates/render/src/lib.rs @@ -7,20 +7,24 @@ //! and the only one external code should interact with. //! (Excluding data structs, like [`ObjectSprite`]) -mod anchoredposition; -mod datastructs; mod globaluniform; mod gpustate; mod pipeline; +mod positionanchor; +mod renderinput; +mod renderstate; mod shaderprocessor; mod starfield; mod texturearray; mod ui; mod vertexbuffer; -pub use anchoredposition::PositionAnchor; -pub use datastructs::RenderInput; +use renderstate::*; + pub use gpustate::GPUState; +pub use positionanchor::PositionAnchor; +pub use renderinput::RenderInput; + use nalgebra::Matrix4; /// Shader entry points diff --git a/crates/render/src/anchoredposition.rs b/crates/render/src/positionanchor.rs similarity index 83% rename from crates/render/src/anchoredposition.rs rename to crates/render/src/positionanchor.rs index 08a2922..262172a 100644 --- a/crates/render/src/anchoredposition.rs +++ b/crates/render/src/positionanchor.rs @@ -28,6 +28,14 @@ pub enum PositionAnchor { /// Position of this sprite's ne corner, /// relative to the ne corner of the window. NeNe, + + /// Position of this sprite's center, + /// relative to the center of the window. + CC, + + /// Position of this sprite's NW corner, + /// relative to the center of the window. + CNw, } // These offsets are implemented in wgsl shaders. @@ -42,6 +50,8 @@ impl PositionAnchor { Self::NwSw => 3, Self::NwSe => 4, Self::NeNe => 5, + Self::CC => 6, + Self::CNw => 7, } } } diff --git a/crates/render/src/renderinput.rs b/crates/render/src/renderinput.rs new file mode 100644 index 0000000..127e80b --- /dev/null +++ b/crates/render/src/renderinput.rs @@ -0,0 +1,36 @@ +use galactica_content::{Content, SystemHandle}; +use galactica_playeragent::PlayerAgent; +use galactica_system::phys::{ParticleBuilder, PhysSim}; +use galactica_util::timing::Timing; +use nalgebra::Vector2; + +/// Bundles parameters passed to a single call to GPUState::render +pub struct RenderInput<'a> { + /// Camera position, in world units + pub camera_pos: Vector2, + + /// Player ship data + pub player: &'a PlayerAgent, + + /// The system we're currently in + pub current_system: SystemHandle, + + /// Height of screen, in world units + pub camera_zoom: f32, + + /// The world state to render + pub systemsim: &'a PhysSim, + + // TODO: handle overflow. is it a problem? + /// The current time, in seconds + pub current_time: f32, + + /// Game content + pub ct: &'a Content, + + /// Particles to spawn during this frame + pub particles: &'a Vec, + + /// Time we spent in each part of the game loop + pub timing: Timing, +} diff --git a/crates/render/src/renderstate.rs b/crates/render/src/renderstate.rs new file mode 100644 index 0000000..5ee0aa0 --- /dev/null +++ b/crates/render/src/renderstate.rs @@ -0,0 +1,237 @@ +use galactica_content::Content; +use galactica_util::constants::{ + OBJECT_SPRITE_INSTANCE_LIMIT, PARTICLE_SPRITE_INSTANCE_LIMIT, RADIALBAR_SPRITE_INSTANCE_LIMIT, + UI_SPRITE_INSTANCE_LIMIT, +}; +use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer}; +use std::rc::Rc; +use wgpu::BufferAddress; +use winit::window::Window; + +use crate::{ + globaluniform::GlobalUniform, + vertexbuffer::{ + consts::{SPRITE_INDICES, SPRITE_VERTICES}, + types::{ + ObjectInstance, ParticleInstance, RadialBarInstance, StarfieldInstance, TexturedVertex, + UiInstance, + }, + BufferObject, VertexBuffer, + }, +}; + +/// Vertex buffers +pub(crate) struct VertexBuffers { + // Keeps track of length of each buffer + // Most of these are reset on each frame. + // + // The exception is particle_counter, which + // is never reset, and loops back to zero once + // it exceeds buffer length + object_counter: BufferAddress, + ui_counter: BufferAddress, + particle_counter: BufferAddress, + radialbar_counter: BufferAddress, + starfield_counter: BufferAddress, + starfield_limit: BufferAddress, + + object: Rc, + starfield: Rc, + ui: Rc, + particle: Rc, + radialbar: Rc, +} + +impl<'a> VertexBuffers { + pub fn new(device: &wgpu::Device, ct: &Content) -> Self { + Self { + object_counter: 0, + ui_counter: 0, + particle_counter: 0, + radialbar_counter: 0, + starfield_counter: 0, + starfield_limit: ct.get_config().starfield_instance_limit, + + object: Rc::new(VertexBuffer::new::( + "object", + &device, + Some(SPRITE_VERTICES), + Some(SPRITE_INDICES), + OBJECT_SPRITE_INSTANCE_LIMIT, + )), + + starfield: Rc::new(VertexBuffer::new::( + "starfield", + &device, + Some(SPRITE_VERTICES), + Some(SPRITE_INDICES), + ct.get_config().starfield_instance_limit, + )), + + ui: Rc::new(VertexBuffer::new::( + "ui", + &device, + Some(SPRITE_VERTICES), + Some(SPRITE_INDICES), + UI_SPRITE_INSTANCE_LIMIT, + )), + + particle: Rc::new(VertexBuffer::new::( + "particle", + &device, + Some(SPRITE_VERTICES), + Some(SPRITE_INDICES), + PARTICLE_SPRITE_INSTANCE_LIMIT, + )), + + radialbar: Rc::new(VertexBuffer::new::( + "radial bar", + &device, + Some(SPRITE_VERTICES), + Some(SPRITE_INDICES), + 10, + )), + } + } + + pub fn get_ui(&'a self) -> &'a Rc { + &self.ui + } + + pub fn get_object(&'a self) -> &'a Rc { + &self.object + } + + pub fn get_particle(&'a self) -> &'a Rc { + &self.particle + } + + pub fn get_radialbar(&'a self) -> &'a Rc { + &self.radialbar + } + + pub fn get_starfield(&'a self) -> &'a Rc { + &self.starfield + } +} + +/// Renderer state. A reference to this struct is often passed to helper functions. +pub(crate) struct RenderState { + pub window: Window, + pub window_size: winit::dpi::PhysicalSize, + pub window_aspect: f32, + + pub queue: wgpu::Queue, + pub global_uniform: GlobalUniform, + pub vertex_buffers: VertexBuffers, + + pub text_font_system: FontSystem, + pub text_cache: SwashCache, + pub text_atlas: TextAtlas, + pub text_renderer: TextRenderer, +} + +impl RenderState { + /// Prepare this state for a new frame + pub fn frame_reset(&mut self) { + self.vertex_buffers.object_counter = 0; + self.vertex_buffers.ui_counter = 0; + self.vertex_buffers.radialbar_counter = 0 + } + + pub fn push_ui_buffer(&mut self, instance: UiInstance) { + // Enforce buffer limit + if self.vertex_buffers.ui_counter as u64 > 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(&[instance]), + ); + self.vertex_buffers.ui_counter += 1; + } + + pub fn get_ui_counter(&self) -> u32 { + self.vertex_buffers.ui_counter as u32 + } + + pub fn push_object_buffer(&mut self, instance: ObjectInstance) { + // Enforce buffer limit + if self.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT { + // TODO: no panic, handle this better. + panic!("Object limit exceeded!") + } + + self.queue.write_buffer( + &self.vertex_buffers.object.instances, + ObjectInstance::SIZE * self.vertex_buffers.object_counter, + bytemuck::cast_slice(&[instance]), + ); + self.vertex_buffers.object_counter += 1; + } + + pub fn get_object_counter(&self) -> u32 { + self.vertex_buffers.object_counter as u32 + } + + pub fn push_radialbar_buffer(&mut self, instance: RadialBarInstance) { + // Enforce buffer limit + if self.vertex_buffers.radialbar_counter as u64 > RADIALBAR_SPRITE_INSTANCE_LIMIT { + // TODO: no panic, handle this better. + panic!("Radialbar sprite limit exceeded!") + } + + self.queue.write_buffer( + &self.vertex_buffers.radialbar.instances, + RadialBarInstance::SIZE * self.vertex_buffers.radialbar_counter, + bytemuck::cast_slice(&[instance]), + ); + self.vertex_buffers.radialbar_counter += 1; + } + + pub fn get_radialbar_counter(&self) -> u32 { + self.vertex_buffers.radialbar_counter as u32 + } + + pub fn reset_starfield_counter(&mut self) { + self.vertex_buffers.starfield_counter = 0; + } + + pub fn push_starfield_buffer(&mut self, instance: StarfieldInstance) { + // Enforce buffer limit + // This should never happen, since starfield generator checks array size + if self.vertex_buffers.starfield_counter as u64 > self.vertex_buffers.starfield_limit { + panic!("Starfield sprite limit exceeded!") + } + + self.queue.write_buffer( + &self.vertex_buffers.starfield.instances, + StarfieldInstance::SIZE * self.vertex_buffers.starfield_counter, + bytemuck::cast_slice(&[instance]), + ); + self.vertex_buffers.starfield_counter += 1; + } + + pub fn get_starfield_counter(&self) -> u32 { + self.vertex_buffers.starfield_counter as u32 + } + + pub fn push_particle_buffer(&mut self, instance: ParticleInstance) { + self.queue.write_buffer( + &self.vertex_buffers.particle.instances, + ParticleInstance::SIZE * self.vertex_buffers.particle_counter, + bytemuck::cast_slice(&[instance]), + ); + self.vertex_buffers.particle_counter += 1; + if self.vertex_buffers.particle_counter == PARTICLE_SPRITE_INSTANCE_LIMIT { + self.vertex_buffers.particle_counter = 0; + } + } + + //pub fn get_particle_counter(&self) -> u32 { + // self.vertex_buffers.particle_counter as u32 + //} +} diff --git a/crates/render/src/starfield.rs b/crates/render/src/starfield.rs index a8f1842..6119c26 100644 --- a/crates/render/src/starfield.rs +++ b/crates/render/src/starfield.rs @@ -2,10 +2,7 @@ use galactica_content::Content; use nalgebra::{Point2, Point3, Vector2, Vector3}; use rand::{self, Rng}; -use crate::{ - datastructs::RenderState, - vertexbuffer::{types::StarfieldInstance, BufferObject}, -}; +use crate::{vertexbuffer::types::StarfieldInstance, RenderState}; pub(crate) struct StarfieldStar { /// Star coordinates, in world space. @@ -23,15 +20,11 @@ pub(crate) struct StarfieldStar { pub(crate) struct Starfield { stars: Vec, - pub instance_count: u32, } impl Starfield { pub fn new() -> Self { - Self { - stars: Vec::new(), - instance_count: 0, - } + Self { stars: Vec::new() } } pub fn regenerate(&mut self, ct: &Content) { @@ -97,24 +90,19 @@ impl Starfield { } // Add all tiles to buffer - self.instance_count = 0; // Keep track of buffer index + state.reset_starfield_counter(); for x in (-nw_tile.x)..=nw_tile.x { for y in (-nw_tile.y)..=nw_tile.y { let offset = Vector3::new(sz * x as f32, sz * y as f32, 0.0); for s in &self.stars { - state.queue.write_buffer( - &state.vertex_buffers.starfield.instances, - StarfieldInstance::SIZE * self.instance_count as u64, - bytemuck::cast_slice(&[StarfieldInstance { - position: (s.pos + offset).into(), - size: s.size, - tint: s.tint.into(), - }]), - ); - self.instance_count += 1; - - // instance_count is guaranteed to stay within buffer limits, + // This shouldn't ever panic. + // instance count is guaranteed to stay within buffer limits, // this is guaranteed by previous checks. + state.push_starfield_buffer(StarfieldInstance { + position: (s.pos + offset).into(), + size: s.size, + tint: s.tint.into(), + }); } } } diff --git a/crates/render/src/ui/fpsindicator.rs b/crates/render/src/ui/fpsindicator.rs index 93ab9c0..6e5b838 100644 --- a/crates/render/src/ui/fpsindicator.rs +++ b/crates/render/src/ui/fpsindicator.rs @@ -1,6 +1,6 @@ use glyphon::{Attrs, Buffer, Color, Family, Metrics, Shaping, TextArea, TextBounds}; -use crate::{datastructs::RenderState, RenderInput}; +use crate::{RenderInput, RenderState}; pub(super) struct FpsIndicator { buffer: Buffer,