Skip to content

Commit

Permalink
performance improvements (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
TitanNano authored Jun 4, 2024
1 parent e8e970d commit 3453e8c
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 61 deletions.
2 changes: 1 addition & 1 deletion native/src/scripts/spawner/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
mod car_spawner;
mod car_spawner;
60 changes: 38 additions & 22 deletions native/src/scripts/world/buildings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use anyhow::Context;
use derive_debug::Dbg;
use godot::{
builtin::{meta::ToGodot, Array, Dictionary},
engine::{utilities::snappedi, Node, Node3D, Resource, Time},
engine::{Marker3D, Node, Node3D, Resource, Time},
obj::{Gd, NewAlloc},
};
use godot_rust_script::{godot_script_impl, GodotScript, ScriptSignal, Signal};
Expand Down Expand Up @@ -61,6 +61,12 @@ impl Buildings {
}
}

fn world_constants(&self) -> &Gd<Resource> {
self.world_constants
.as_ref()
.expect("world_constants should be set!")
}

pub fn build_async(&mut self, city: Dictionary) {
let city = match crate::world::city_data::City::try_from_dict(&city)
.context("Failed to deserialize city data")
Expand All @@ -76,13 +82,8 @@ impl Buildings {
let buildings = city.buildings;
let tiles = city.tilelist;

self.city_coords_feature = CityCoordsFeature::new(
self.world_constants
.as_ref()
.expect("world_constants should be set!")
.to_owned(),
sea_level,
);
self.city_coords_feature =
CityCoordsFeature::new(self.world_constants().to_owned(), sea_level);

logger::info!("starting to load buildings...");

Expand Down Expand Up @@ -161,15 +162,8 @@ impl Buildings {
// fix z fighting of flat buildings
location.y += 0.1;

let sector_name = {
let x = snappedi(tile_coords.0 as f64, 10);
let y = snappedi(tile_coords.1 as f64, 10);

format!("{}_{}", x, y)
};

let (_, insert_time) = with_timing(|| {
self.get_sector(sector_name)
self.get_sector(tile_coords)
.add_child_ex(instance.clone())
.force_readable_name(true)
.done();
Expand All @@ -186,7 +180,8 @@ impl Buildings {
instance.set_owner(root);
});

let (_, translate_time) = with_timing(|| instance.cast::<Node3D>().translate(location));
let (_, translate_time) =
with_timing(|| instance.cast::<Node3D>().set_global_position(location));

if instance_time > 100 {
logger::warn!("\"{}\" is very slow to instantiate!", name);
Expand All @@ -201,15 +196,36 @@ impl Buildings {
}
}

fn get_sector(&mut self, name: String) -> Gd<Node> {
/// sector coordinates are expected to align with a step of 10
fn get_sector(&mut self, tile_coords: (u32, u32)) -> Gd<Node3D> {
const SECTOR_SIZE: u32 = 32;

let sector_coords = (
(tile_coords.0 / SECTOR_SIZE) * SECTOR_SIZE,
(tile_coords.1 / SECTOR_SIZE) * SECTOR_SIZE,
);

let sector_name = {
let (x, y) = sector_coords;

format!("{}_{}", x, y)
};

self.base
.get_node_or_null(name.to_godot().into())
.get_node_or_null(sector_name.to_godot().into())
.map(Gd::cast)
.unwrap_or_else(|| {
let mut sector = Node::new_alloc();
let mut sector: Gd<Node3D> = Marker3D::new_alloc().upcast();

sector.set_name(sector_name.to_godot());

sector.set_name(name.to_godot());
self.base.add_child(sector.clone().upcast());

self.base.add_child(sector.clone());
sector.translate(self.city_coords_feature.get_world_coords(
sector_coords.0 + (SECTOR_SIZE / 2),
sector_coords.1 + (SECTOR_SIZE / 2),
0,
));

if let Some(root) = self
.base
Expand Down
115 changes: 80 additions & 35 deletions native/src/terrain_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use godot::engine::{ArrayMesh, Material, SurfaceTool};
use godot::prelude::meta::GodotType;
use godot::prelude::*;

use std::cmp::{max, min};
use std::collections::HashMap;
use std::ops::Deref;
use std::sync::Arc;
Expand Down Expand Up @@ -41,8 +40,8 @@ impl<T: GodotType> Deref for Shared<T> {
}

impl<T: GodotType> Shared<T> {
fn inner(self) -> T {
self.0
fn inner(&self) -> &T {
&self.0
}
}

Expand Down Expand Up @@ -162,6 +161,37 @@ fn create_tilelist_key(x: u16, y: u16) -> Variant {
key.to_variant()
}

struct ChunkConfig {
tile_coords: (u16, u16),
size: u16,
}

#[derive(GodotClass)]
pub struct TerrainChunk {
config: ChunkConfig,
mesh: Shared<Gd<ArrayMesh>>,
}

#[godot_api]
impl TerrainChunk {
#[func]
pub fn mesh(&self) -> Gd<ArrayMesh> {
self.mesh.inner().to_owned()
}

#[func]
pub fn tile_coords(&self) -> Array<u16> {
let (x, y) = self.config.tile_coords;

Array::from(&[x, y])
}
}

struct ChunkSurfaces {
config: ChunkConfig,
surfaces: SurfaceMap,
}

struct ThreadContext<'a> {
tile_size: u8,
city_size: u16,
Expand All @@ -179,6 +209,7 @@ pub struct TerrainBuilder {
city_size: u16,
tile_height: u8,
sea_level: u16,
chunk_size: u16,
rotation: Gd<TerrainRotation>,
tilelist: Dictionary,
materials: Dictionary,
Expand All @@ -197,6 +228,7 @@ impl TerrainBuilder {
city_size: 0,
tile_height: 8,
sea_level: 0,
chunk_size: 32,
rotation,
tilelist,
materials,
Expand All @@ -223,6 +255,11 @@ impl TerrainBuilder {
self.sea_level = value;
}

#[func]
fn chunk_size(&self) -> u16 {
self.chunk_size
}

fn tilelist(&self) -> Shared<Dictionary> {
Shared(self.tilelist.clone())
}
Expand Down Expand Up @@ -328,14 +365,17 @@ impl TerrainBuilder {

fn build_chunk_vertices(
context: &ThreadContext,
chunk: (u16, u16, u16, u16),
) -> (SurfaceMap, Vec<VertexRef>) {
chunk: ChunkConfig,
) -> (ChunkSurfaces, Vec<VertexRef>) {
let mut ybuffer: HashMapYBuffer<VertexRef> = YBuffer::new();
let mut surfaces = SurfaceMap::new();
let mut vertices = vec![];
let mut edge_buffer = vec![];

let (lower_y, upper_y, lower_x, upper_x) = chunk;
let lower_y = chunk.tile_coords.1;
let lower_x = chunk.tile_coords.0;
let upper_y = lower_y + chunk.size;
let upper_x = lower_x + chunk.size;

for y in lower_y..upper_y {
for x in lower_x..upper_x {
Expand Down Expand Up @@ -392,18 +432,32 @@ impl TerrainBuilder {
vertex.set_normal(normal);
});

(surfaces, edge_buffer)
(
ChunkSurfaces {
surfaces,
config: chunk,
},
edge_buffer,
)
}

fn build_terain_chunk(context: &ThreadContext, surfaces: SurfaceMap) -> Shared<Gd<ArrayMesh>> {
fn build_terain_chunk(context: &ThreadContext, chunk: ChunkSurfaces) -> TerrainChunk {
let mut generator = SurfaceTool::new_gd();
let mut mesh = ArrayMesh::new_gd();
let mut vertex_count = 0;

for (surface_type, surface) in surfaces {
for (surface_type, surface) in chunk.surfaces {
generator.clear();
generator.begin(PrimitiveType::TRIANGLES);

// calculate global offset. Vertex contains the wold coordinates and we have to subtract the
// offset to get the model coordinates.
let world_offset = Vector3 {
x: f32::from(chunk.config.tile_coords.0 * u16::from(context.tile_size)),
y: 0.0,
z: f32::from(chunk.config.tile_coords.1 * u16::from(context.tile_size)),
};

for vertex_cell in surface {
let vertex = Arc::try_unwrap(vertex_cell)
.expect("too many refs to vertex")
Expand All @@ -423,7 +477,7 @@ impl TerrainBuilder {
generator.set_color(Color::from_rgb(1.0, 1.0, 1.0));
}

generator.add_vertex(vertex.into());
generator.add_vertex(Vector3::from(vertex) - world_offset);

vertex_count += 1;
}
Expand All @@ -450,30 +504,29 @@ impl TerrainBuilder {
godot_print!("generated {} vertices for terain", vertex_count);
godot_print!("done generating surfaces {}ms", 0.0);

Shared(mesh)
TerrainChunk {
mesh: Shared(mesh),
config: chunk.config,
}
}

#[func]
pub fn build_terain_async(&self) -> Array<Gd<ArrayMesh>> {
let chunk_size = 16;
pub fn build_terain_async(&self) -> Array<Gd<TerrainChunk>> {
let chunk_size = self.chunk_size;

// we need to be certain that we have a compatible city size
assert!((self.city_size % chunk_size) == 0);

let chunk_count = self.city_size / chunk_size;
let timer = Instant::now();
let mut chunks: Vec<(u16, u16, u16, u16)> = Vec::with_capacity(chunk_count as usize);

for chunk_y in 0..chunk_count {
for chunk_x in 0..chunk_count {
let chunk_lower_y = max(chunk_y * chunk_size, 0);
let chunk_upper_y = min((chunk_y + 1) * chunk_size, self.city_size);
let chunk_lower_x = max(chunk_x * chunk_size, 0);
let chunk_upper_x = min((chunk_x + 1) * chunk_size, self.city_size);

chunks.push((chunk_lower_y, chunk_upper_y, chunk_lower_x, chunk_upper_x));
}
}
let chunks: Vec<_> = (0..chunk_count)
.flat_map(|y| (0..chunk_count).map(move |x| (x, y)))
.map(|(x, y)| ChunkConfig {
tile_coords: (x * chunk_size, y * chunk_size),
size: chunk_size,
})
.collect();

let rotation = self.rotation().bind();
let context = self.thread_context(rotation.deref());
Expand All @@ -485,14 +538,14 @@ impl TerrainBuilder {

stitch_chunk_seams(chunk_edge_vertices);

let result: Vec<Shared<Gd<ArrayMesh>>> = surface_chunks
let result: Vec<_> = surface_chunks
.into_par_iter()
.map(|chunk| Self::build_terain_chunk(&context, chunk))
.collect();

godot_print!("terrain build time: {}ms", timer.elapsed().as_millis());

result.into_iter().map(|mesh| mesh.inner()).collect()
result.into_iter().map(Gd::from_object).collect()
}
}

Expand Down Expand Up @@ -523,14 +576,6 @@ impl TerrainBuilderFactory {
rotation: Gd<TerrainRotation>,
materials: Dictionary,
) -> Gd<TerrainBuilder> {
Gd::from_object(TerrainBuilder {
tile_size: 16,
city_size: 0,
tile_height: 8,
sea_level: 0,
rotation,
tilelist,
materials,
})
TerrainBuilder::new(tilelist, rotation, materials)
}
}
2 changes: 1 addition & 1 deletion resources/Animations/Helicopter.tres
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ tracks/0/loop_wrap = true
tracks/0/keys = {
"clips": [{
"end_offset": 0.0,
"start_offset": 0.0,
"start_offset": 0.3,
"stream": ExtResource("1_vcbvg")
}],
"times": PackedFloat32Array(0)
Expand Down
3 changes: 3 additions & 0 deletions resources/Objects/Helis/Helicopter.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ strength = null

[node name="RotorAudioTree" type="AnimationTree" parent="."]
root_node = NodePath(".")
libraries = {
"": ExtResource("5_ayahk")
}
tree_root = ExtResource("4_8iee2")
advance_expression_base_node = NodePath("")
anim_player = NodePath("../RotorAudioPlayer")
Expand Down
3 changes: 3 additions & 0 deletions resources/TestScenes/ocean.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ transform = Transform3D(1, -3.48787e-16, 3.48787e-16, 3.48787e-16, 1, -3.48787e-
mesh = SubResource("1")
surface_material_override/0 = ExtResource("2")

[node name="Camera3D" type="Camera3D" parent="Plane"]
transform = Transform3D(1, 1.44283e-16, 3.62448e-16, -3.62448e-16, 0.687252, 0.726419, -1.44283e-16, -0.726419, 0.687252, -1.40621e-14, 59.6592, 99.9748)

[node name="Plane2" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.08165e-12, 30.5047, -512)
mesh = SubResource("1")
Expand Down
Loading

0 comments on commit 3453e8c

Please sign in to comment.