From 7d3e97da5a5da3f22d58891ea2f6d2144734592d Mon Sep 17 00:00:00 2001 From: Tobias Berger Date: Wed, 16 Nov 2022 13:55:30 +0100 Subject: [PATCH] Cache rendered maps Plus some small tweaks --- planet/src/biome.rs | 2 +- planet/src/math_util.rs | 7 ++++ src/main.rs | 4 +- src/planet_renderer.rs | 83 +++++++++++++++++++++++++++++++++-------- 4 files changed, 78 insertions(+), 18 deletions(-) diff --git a/planet/src/biome.rs b/planet/src/biome.rs index 1596c0b..8723bf5 100644 --- a/planet/src/biome.rs +++ b/planet/src/biome.rs @@ -32,7 +32,7 @@ impl From for BiomeStats { BiomeType::IceCap => BiomeStats { name: "Ice Cap".into(), #[cfg(feature = "render")] - color: Color::rgb_u8(253, 244, 235), + color: Color::WHITE, min_altitude: World::MIN_ALTITUDE, max_altitude: World::MAX_ALTITUDE, min_rainfall: World::MIN_RAINFALL, diff --git a/planet/src/math_util.rs b/planet/src/math_util.rs index a8b2889..bcdea3f 100644 --- a/planet/src/math_util.rs +++ b/planet/src/math_util.rs @@ -35,6 +35,7 @@ impl Display for CartesianError { } } +#[must_use] pub fn cartesian_coordinates( alpha: f32, mut beta: f32, @@ -61,6 +62,7 @@ pub fn cartesian_coordinates( )) } +#[must_use] pub fn random_point_in_sphere(rng: &mut StdRng, radius: f32) -> Vec3A { // https://karthikkaranth.me/blog/generating-random-points-in-a-sphere/#better-choice-of-spherical-coordinates @@ -85,14 +87,19 @@ pub fn random_point_in_sphere(rng: &mut StdRng, radius: f32) -> Vec3A { ) } +#[inline(always)] +#[must_use] pub fn mix_values(a: f32, b: f32, weight_b: f32) -> f32 { (b * weight_b) + (a * (1.0 - weight_b)) } pub trait RepeatNum { + #[must_use] fn repeat(self, length: Self) -> Self; } impl RepeatNum for f32 { + #[inline(always)] + #[must_use] fn repeat(self, length: f32) -> f32 { f32::clamp(self - (self / length).floor() * length, 0.0, length) } diff --git a/src/main.rs b/src/main.rs index 57f8f88..9711449 100644 --- a/src/main.rs +++ b/src/main.rs @@ -233,6 +233,7 @@ fn update_gui(world: &mut World) { fn redraw_map( mut should_redraw: ResMut, world_manager: Res, + mut world_renderer: ResMut, render_settings: Res, mut images: ResMut>, mut map_sprite: Query<&mut Sprite>, @@ -267,7 +268,7 @@ fn redraw_map( height: world.height, ..default() }); - map_image.data = world_manager.map_color_bytes(render_settings); + map_image.data = world_renderer.map_color_bytes(world_manager, render_settings); map_sprite.single_mut().custom_size = Some(Vec2 { x: (world.width * WORLD_SCALE as u32) as f32, y: (world.height * WORLD_SCALE as u32) as f32, @@ -297,6 +298,7 @@ fn main() -> Result<(), Box> { .insert_resource(OpenedWindows::default()) .insert_resource(WorldRenderSettings::default()) .insert_resource(ShouldRedraw::default()) + .insert_resource(WorldRenderer::default()) .add_startup_system(generate_graphics) .add_system(update_gui) .add_system(update_cursor_map_position) diff --git a/src/planet_renderer.rs b/src/planet_renderer.rs index b212bfe..d489e8e 100644 --- a/src/planet_renderer.rs +++ b/src/planet_renderer.rs @@ -1,10 +1,6 @@ use { crate::macros::iterable_enum_stringify, - bevy::{ - asset::HandleId, - prelude::{Color, Resource}, - utils::HashSet, - }, + bevy::{asset::HandleId, prelude::*, utils::HashSet}, planet::{BiomeStats, TerrainCell, World, WorldManager}, }; @@ -166,34 +162,89 @@ fn coastline_color(world: &World, cell: &TerrainCell) -> Color { COASTLINE_PALETTE[0] } } -pub trait WorldRenderer { - fn map_color_bytes(&self, render_settings: &WorldRenderSettings) -> Vec; - fn generate_color(&self, cell: &TerrainCell, render_settings: &WorldRenderSettings) -> Color; + +const CACHE_SIZE: usize = + WorldOverlay::ITEM_COUNT * WorldOverlay::ITEM_COUNT * WorldView::ITEM_COUNT; + +#[derive(Default, Resource)] +pub struct WorldRenderer { + cached_world_seed: u32, + cache: [Option>; CACHE_SIZE], } -impl WorldRenderer for WorldManager { + +impl WorldRenderer { + fn cache_index(render_settings: &WorldRenderSettings) -> usize { + let view_num = match render_settings.view { + WorldView::Biomes => 0, + WorldView::Topography => 1, + WorldView::Coastlines => 2, + }; + let mut overlay_num = 0; + for overlay in render_settings.visible_overlays.iter() { + overlay_num |= match overlay { + WorldOverlay::Temperature => 1, + WorldOverlay::Rainfall => 2, + }; + } + #[cfg(feature = "logging")] + debug!(overlay_num, view_num); + (view_num << WorldOverlay::ITEM_COUNT) | overlay_num + } + #[must_use] - fn map_color_bytes(&self, render_settings: &WorldRenderSettings) -> Vec { - let Some(world) = self.get_world() else { + pub fn map_color_bytes( + &mut self, + world_manager: &WorldManager, + render_settings: &WorldRenderSettings, + ) -> Vec { + let Some(world) = world_manager.get_world() else { return vec![]; }; - world + + let cache_index = WorldRenderer::cache_index(render_settings); + #[cfg(feature = "logging")] + debug!(cache_index); + assert!( + cache_index < CACHE_SIZE, + "Generated cache index too large for render cache" + ); + if world.seed != self.cached_world_seed { + self.cache = default(); + self.cached_world_seed = world.seed; + } + if let Some(cached) = &self.cache[cache_index] { + return cached.clone(); + } + + let bytes: Vec<_> = world .terrain .iter() .rev() .flatten() .flat_map(|cell| { - self.generate_color(cell, render_settings) + self.generate_color(world_manager, cell, render_settings) .as_rgba_f32() .iter() .flat_map(|num| num.to_le_bytes()) .collect::>() }) - .collect() + .collect(); + let result = bytes.clone(); + self.cache[cache_index] = Some(bytes); + + result } #[must_use] - fn generate_color(&self, cell: &TerrainCell, render_settings: &WorldRenderSettings) -> Color { - let world = self.get_world().expect("No world in generate_color"); + pub fn generate_color( + &self, + world_manager: &WorldManager, + cell: &TerrainCell, + render_settings: &WorldRenderSettings, + ) -> Color { + let world = world_manager + .get_world() + .expect("No world in generate_color"); let base_color = match render_settings.view { WorldView::Biomes => biome_color(world, cell), WorldView::Topography => altitude_contour_color(world, cell.altitude),