Skip to content

Commit

Permalink
Optimize mesh tangent generation (Many times faster) & SIMD for the n…
Browse files Browse the repository at this point in the history
…oise application (30% reduction in time)
  • Loading branch information
TheGrimsey committed Oct 8, 2024
1 parent 3967c80 commit b2187cc
Show file tree
Hide file tree
Showing 5 changed files with 350 additions and 53 deletions.
22 changes: 20 additions & 2 deletions examples/many_tiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ use bevy_editor_pls::{default_windows::cameras::ActiveEditorCamera, editor_windo
use bevy_lookup_curve::{editor::{LookupCurveEditor, LookupCurveEguiEditor}, LookupCurve};
use bevy_world_seed::{
material::{
GlobalTexturingRules, TerrainTexturingSettings, TexturingRule, TexturingRuleEvaluator,
}, noise::{NoiseCache, TerrainNoiseDetailLayer, TerrainNoiseSettings, TerrainNoiseSplineLayer}, terrain::{Terrain, TileToTerrain}, RebuildTile, TerrainPlugin, TerrainSettings
GlobalTexturingRules, TerrainTextureRebuildQueue, TerrainTexturingSettings, TexturingRule, TexturingRuleEvaluator
}, meshing::TerrainMeshRebuildQueue, noise::{NoiseCache, TerrainNoiseDetailLayer, TerrainNoiseSettings, TerrainNoiseSplineLayer}, terrain::{Terrain, TileToTerrain}, RebuildTile, TerrainHeightRebuildQueue, TerrainPlugin, TerrainSettings
};

fn main() {
Expand Down Expand Up @@ -193,6 +193,24 @@ impl EditorWindow for NoiseDebugWindow {

world.send_event_batch(tiles.into_iter().map(RebuildTile));
}

let heights_queue = world.resource::<TerrainHeightRebuildQueue>();
let mesh_queue = world.resource::<TerrainMeshRebuildQueue>();
let texture_queue = world.resource::<TerrainTextureRebuildQueue>();

if !heights_queue.is_empty() || !mesh_queue.is_empty() || !texture_queue.is_empty() {
ui.heading("Queued");

ui.columns(3, |ui| {
ui[0].label("Heights");
ui[1].label("Meshes");
ui[2].label("Textures");

ui[0].label(heights_queue.count().to_string());
ui[1].label(mesh_queue.count().to_string());
ui[2].label(texture_queue.count().to_string());
});
}

let mut query_state = world.query_filtered::<&GlobalTransform, With<ActiveEditorCamera>>();
let lookup_curves = world.resource::<Assets<LookupCurve>>();
Expand Down
56 changes: 39 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::num::NonZeroU8;

use bevy::{
app::{App, Plugin, PostUpdate}, asset::Assets, log::info_span, math::{FloatExt, IVec2, Vec2, Vec3, Vec3Swizzles}, prelude::{
any_with_component, resource_changed, AnyOf, Component, Deref, Event, EventReader, EventWriter, IntoSystemConfigs, Local, Query, ReflectResource, Res, ResMut, Resource, SystemSet, TransformSystem
any_with_component, resource_changed, AnyOf, Component, Deref, DetectChanges, Event, EventReader, EventWriter, IntoSystemConfigs, Local, Query, ReflectResource, Res, ResMut, Resource, SystemSet, TransformSystem
}, reflect::Reflect, transform::components::GlobalTransform
};
use bevy_lookup_curve::{LookupCurve, LookupCurvePlugin};
Expand All @@ -22,7 +22,7 @@ use modifiers::{
ModifierStrengthLimitProperty, ShapeModifier, TerrainSplineCached, TerrainSplineProperties,
TerrainSplineShape, TileToModifierMapping,
};
use noise::{NoiseCache, TerrainNoiseDetailLayer, TerrainNoiseSettings};
use noise::{apply_noise_simd, NoiseCache, TerrainNoiseDetailLayer, TerrainNoiseSettings};
use snap_to_terrain::TerrainSnapToTerrainPlugin;
use terrain::{insert_components, update_tiling, Holes, Terrain, TileToTerrain};
use utils::{distance_squared_to_line_segment, index_to_x_z};
Expand All @@ -35,7 +35,7 @@ mod debug_draw;
#[cfg(feature = "rendering")]
pub mod material;
#[cfg(feature = "rendering")]
mod meshing;
pub mod meshing;
pub mod snap_to_terrain;

pub mod noise;
Expand Down Expand Up @@ -127,6 +127,8 @@ impl Plugin for TerrainPlugin {
app.register_type::<TerrainNoiseDetailLayer>()
.register_type::<TerrainNoiseSettings>();
}

app.init_resource::<TerrainHeightRebuildQueue>();
}
}

Expand Down Expand Up @@ -172,6 +174,21 @@ pub struct TileHeightsRebuilt(pub IVec2);
#[derive(Component, Deref, Debug)]
pub struct Heights(Box<[f32]>);

/// Queue of terrain tiles which [`Heights`] are to be rebuilt.
#[derive(Resource, Default)]
pub struct TerrainHeightRebuildQueue(Vec<IVec2>);
impl TerrainHeightRebuildQueue {
pub fn get(&self) -> &[IVec2] {
&self.0
}
pub fn count(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}

fn update_terrain_heights(
terrain_noise_layers: Option<Res<TerrainNoiseSettings>>,
shape_modifier_query: Query<(
Expand All @@ -192,15 +209,27 @@ fn update_terrain_heights(
terrain_settings: Res<TerrainSettings>,
tile_to_modifier: Res<TileToModifierMapping>,
tile_to_terrain: Res<TileToTerrain>,
mut tile_generate_queue: Local<Vec<IVec2>>,
mut tile_generate_queue: ResMut<TerrainHeightRebuildQueue>,
mut noise_cache: ResMut<NoiseCache>,
mut event_reader: EventReader<RebuildTile>,
mut tile_rebuilt_events: EventWriter<TileHeightsRebuilt>,
lookup_curves: Res<Assets<LookupCurve>>
lookup_curves: Res<Assets<LookupCurve>>,
mut noise_spline_index_cache: Local<Vec<u32>>,
mut noise_detail_index_cache: Local<Vec<u32>>
) {
// Cache indexes into the noise cache for each terrain noise.
// Saves having to do the checks and insertions for every iteration when applying the noise.
if let Some(terrain_noise_layers) = terrain_noise_layers.as_ref().filter(|noise_layers| noise_layers.is_changed()) {
noise_spline_index_cache.clear();
noise_spline_index_cache.extend(terrain_noise_layers.splines.iter().map(|spline| noise_cache.get_simplex_index(spline.seed) as u32));

noise_detail_index_cache.clear();
noise_detail_index_cache.extend(terrain_noise_layers.layers.iter().map(|layer| noise_cache.get_simplex_index(layer.seed) as u32));
}

for RebuildTile(tile) in event_reader.read() {
if !tile_generate_queue.contains(tile) {
tile_generate_queue.push(*tile);
if !tile_generate_queue.0.contains(tile) {
tile_generate_queue.0.push(*tile);
}
}

Expand All @@ -221,10 +250,10 @@ fn update_terrain_heights(
let inv_tile_size_scale = scale * (7.0 / tile_size);

let tiles_to_generate = tile_generate_queue
.len()
.count()
.min(terrain_settings.max_tile_updates_per_frame.get() as usize);

for tile in tile_generate_queue.drain(..tiles_to_generate) {
for tile in tile_generate_queue.0.drain(..tiles_to_generate) {
let Some(tiles) = tile_to_terrain.0.get(&tile) else {
continue;
};
Expand All @@ -241,14 +270,7 @@ fn update_terrain_heights(
// First, set by noise.
if let Some(terrain_noise_layers) = terrain_noise_layers.as_ref() {
let _span = info_span!("Apply noise").entered();
for (i, val) in heights.0.iter_mut().enumerate() {
let (x, z) = index_to_x_z(i, terrain_settings.edge_points as usize);

let vertex_position =
terrain_translation + Vec2::new(x as f32 * scale, z as f32 * scale);

*val += terrain_noise_layers.sample_position(&mut noise_cache, vertex_position, &lookup_curves);
}
apply_noise_simd(&mut heights.0, &terrain_settings, terrain_translation, scale, &noise_cache, &noise_spline_index_cache, &noise_detail_index_cache, &lookup_curves, terrain_noise_layers);
}

// Secondly, set by shape-modifiers.
Expand Down
29 changes: 23 additions & 6 deletions src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use bevy::{
pbr::{ExtendedMaterial, MaterialExtension, MaterialPlugin, StandardMaterial},
prelude::{
default, Commands, Component, Entity, EventReader, GlobalTransform, Image,
IntoSystemConfigs, Local, Mesh, Query, ReflectComponent, ReflectDefault, ReflectResource,
IntoSystemConfigs, Mesh, Query, ReflectComponent, ReflectDefault, ReflectResource,
Res, ResMut, Resource, Shader, With, Without,
},
reflect::Reflect,
Expand Down Expand Up @@ -63,6 +63,8 @@ impl Plugin for TerrainTexturingPlugin {
)
.chain(),
);

app.init_resource::<TerrainTextureRebuildQueue>();
}
}

Expand Down Expand Up @@ -400,6 +402,21 @@ fn insert_texture_map(
});
}

/// Queue of terrain tiles which textures are to be rebuilt.
#[derive(Resource, Default)]
pub struct TerrainTextureRebuildQueue(Vec<IVec2>);
impl TerrainTextureRebuildQueue {
pub fn get(&self) -> &[IVec2] {
&self.0
}
pub fn count(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}

fn update_terrain_texture_maps(
shape_modifier_query: Query<(
&TextureModifierOperation,
Expand All @@ -426,15 +443,15 @@ fn update_terrain_texture_maps(
tile_to_modifier: Res<TileToModifierMapping>,
tile_to_terrain: Res<TileToTerrain>,
mut event_reader: EventReader<TerrainMeshRebuilt>,
mut tile_generate_queue: Local<Vec<IVec2>>,
mut tile_generate_queue: ResMut<TerrainTextureRebuildQueue>,
mut materials: ResMut<Assets<TerrainMaterialExtended>>,
mut images: ResMut<Assets<Image>>,
meshes: Res<Assets<Mesh>>,
texturing_rules: Res<GlobalTexturingRules>,
) {
for TerrainMeshRebuilt(tile) in event_reader.read() {
if !tile_generate_queue.contains(tile) {
tile_generate_queue.push(*tile);
if !tile_generate_queue.0.contains(tile) {
tile_generate_queue.0.push(*tile);
}
}

Expand All @@ -449,13 +466,13 @@ fn update_terrain_texture_maps(
let inv_tile_size_scale = scale * (7.0 / tile_size);

let tiles_to_generate = tile_generate_queue
.len()
.count()
.min(texture_settings.max_tile_updates_per_frame.get() as usize);

tiles_query
.iter_many(
tile_generate_queue
.drain(..tiles_to_generate)
.0.drain(..tiles_to_generate)
.filter_map(|tile| tile_to_terrain.0.get(&tile))
.flatten(),
)
Expand Down
Loading

0 comments on commit b2187cc

Please sign in to comment.