mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'xMAC94x/get_rid_of_useless_threadpool' into 'master'
rayon::join creates a global threadpool, which is only used in /world See merge request veloren/veloren!2251
This commit is contained in:
commit
3ddbb479f2
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -5607,6 +5607,7 @@ version = "0.9.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"hashbrown",
|
||||
"num_cpus",
|
||||
"rayon",
|
||||
"scopeguard",
|
||||
"serde",
|
||||
@ -5766,6 +5767,7 @@ dependencies = [
|
||||
"clap",
|
||||
"crossterm 0.19.0",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
"ron",
|
||||
"serde",
|
||||
"signal-hook 0.3.8",
|
||||
@ -5821,6 +5823,7 @@ dependencies = [
|
||||
"old_school_gfx_glutin_ext",
|
||||
"ordered-float 2.1.1",
|
||||
"rand 0.8.3",
|
||||
"rayon",
|
||||
"rodio",
|
||||
"ron",
|
||||
"serde",
|
||||
|
@ -16,3 +16,7 @@ pub const WATER_DENSITY: f32 = 999.1026;
|
||||
pub const IRON_DENSITY: f32 = 7870.0;
|
||||
// pub const HUMAN_DENSITY: f32 = 1010.0; // real value
|
||||
pub const HUMAN_DENSITY: f32 = 990.0; // value we use to make humanoids gently float
|
||||
// 1 thread might be used for long-running cpu intensive tasks, like chunk
|
||||
// generation. having at least 2 helps not blocking in the main tick here
|
||||
pub const MIN_RECOMMENDED_RAYON_THREADS: usize = 2;
|
||||
pub const MIN_RECOMMENDED_TOKIO_THREADS: usize = 2;
|
||||
|
@ -18,6 +18,7 @@ common-ecs = { package = "veloren-common-ecs", path = "../ecs" }
|
||||
common-base = { package = "veloren-common-base", path = "../base" }
|
||||
|
||||
rayon = "1.5"
|
||||
num_cpus = "1.0"
|
||||
tracing = { version = "0.1", default-features = false }
|
||||
vek = { version = "=0.14.1", features = ["serde"] }
|
||||
|
||||
|
@ -103,6 +103,7 @@ impl State {
|
||||
|
||||
let thread_pool = Arc::new(
|
||||
ThreadPoolBuilder::new()
|
||||
.num_threads(num_cpus::get().max(common::consts::MIN_RECOMMENDED_RAYON_THREADS))
|
||||
.thread_name(move |i| format!("rayon-{}-{}", thread_name_infix, i))
|
||||
.build()
|
||||
.unwrap(),
|
||||
@ -208,8 +209,8 @@ impl State {
|
||||
ecs.insert(Vec::<common::outcome::Outcome>::new());
|
||||
ecs.insert(common::CachedSpatialGrid::default());
|
||||
|
||||
let slow_limit = thread_pool.current_num_threads().max(2) as u64;
|
||||
let slow_limit = slow_limit / 2 + slow_limit / 4;
|
||||
let num_cpu = num_cpus::get() as u64;
|
||||
let slow_limit = (num_cpu / 2 + num_cpu / 4).max(1);
|
||||
tracing::trace!(?slow_limit, "Slow Thread limit");
|
||||
ecs.insert(SlowJobPool::new(slow_limit, Arc::clone(&thread_pool)));
|
||||
|
||||
@ -321,6 +322,8 @@ impl State {
|
||||
/// Get a mutable reference to the internal ECS world.
|
||||
pub fn ecs_mut(&mut self) -> &mut specs::World { &mut self.ecs }
|
||||
|
||||
pub fn thread_pool(&self) -> &Arc<ThreadPool> { &self.thread_pool }
|
||||
|
||||
/// Get a reference to the `TerrainChanges` structure of the state. This
|
||||
/// contains information about terrain state that has changed since the
|
||||
/// last game tick.
|
||||
|
@ -28,6 +28,7 @@ common-net = { package = "veloren-common-net", path = "../common/net" }
|
||||
common-frontend = { package = "veloren-common-frontend", path = "../common/frontend" }
|
||||
|
||||
tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] }
|
||||
num_cpus = "1.0"
|
||||
ansi-parser = "0.7"
|
||||
clap = "2.33"
|
||||
crossterm = "0.19"
|
||||
|
@ -14,7 +14,7 @@ use crate::{
|
||||
cmd::Message, shutdown_coordinator::ShutdownCoordinator, tui_runner::Tui, tuilog::TuiLog,
|
||||
};
|
||||
use clap::{App, Arg, SubCommand};
|
||||
use common::clock::Clock;
|
||||
use common::{clock::Clock, consts::MIN_RECOMMENDED_TOKIO_THREADS};
|
||||
use common_base::span;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use server::{
|
||||
@ -117,9 +117,13 @@ fn main() -> io::Result<()> {
|
||||
path
|
||||
};
|
||||
|
||||
// We don't need that many threads in the async pool, at least 2 but generally
|
||||
// 25% of all available will do
|
||||
// TODO: evaluate std::thread::available_concurrency as a num_cpus replacement
|
||||
let runtime = Arc::new(
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.worker_threads((num_cpus::get() / 4).max(MIN_RECOMMENDED_TOKIO_THREADS))
|
||||
.thread_name_fn(|| {
|
||||
static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst);
|
||||
|
@ -282,21 +282,26 @@ impl Server {
|
||||
state.ecs_mut().insert(AliasValidator::new(banned_words));
|
||||
|
||||
#[cfg(feature = "worldgen")]
|
||||
let (world, index) = World::generate(settings.world_seed, WorldOpts {
|
||||
seed_elements: true,
|
||||
world_file: if let Some(ref opts) = settings.map_file {
|
||||
opts.clone()
|
||||
} else {
|
||||
// Load default map from assets.
|
||||
FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into())
|
||||
let (world, index) = World::generate(
|
||||
settings.world_seed,
|
||||
WorldOpts {
|
||||
seed_elements: true,
|
||||
world_file: if let Some(ref opts) = settings.map_file {
|
||||
opts.clone()
|
||||
} else {
|
||||
// Load default map from assets.
|
||||
FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into())
|
||||
},
|
||||
..WorldOpts::default()
|
||||
},
|
||||
..WorldOpts::default()
|
||||
});
|
||||
&state.thread_pool(),
|
||||
);
|
||||
|
||||
#[cfg(feature = "worldgen")]
|
||||
let map = world.get_map_data(index.as_index_ref());
|
||||
let map = world.get_map_data(index.as_index_ref(), &state.thread_pool());
|
||||
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
let (world, index) = World::generate(settings.world_seed);
|
||||
let (world, index) = World::generate(settings.world_seed, &state.thread_pool());
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
let map = WorldMapMsg {
|
||||
dimensions_lg: Vec2::zero(),
|
||||
|
@ -123,6 +123,7 @@ winres = "0.1"
|
||||
criterion = "0.3"
|
||||
git2 = "0.13"
|
||||
world = {package = "veloren-world", path = "../world"}
|
||||
rayon = "1.5.0"
|
||||
|
||||
[[bench]]
|
||||
harness = false
|
||||
|
@ -13,16 +13,21 @@ const GEN_SIZE: i32 = 4;
|
||||
|
||||
#[allow(clippy::needless_update)] // TODO: Pending review in #587
|
||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
let pool = rayon::ThreadPoolBuilder::new().build().unwrap();
|
||||
// Generate chunks here to test
|
||||
let mut terrain = TerrainGrid::new().unwrap();
|
||||
let (world, index) = World::generate(42, sim::WorldOpts {
|
||||
// NOTE: If this gets too expensive, we can turn it off.
|
||||
// TODO: Consider an option to turn off all erosion as well, or even provide altitude
|
||||
// directly with a closure.
|
||||
seed_elements: true,
|
||||
world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()),
|
||||
..Default::default()
|
||||
});
|
||||
let (world, index) = World::generate(
|
||||
42,
|
||||
sim::WorldOpts {
|
||||
// NOTE: If this gets too expensive, we can turn it off.
|
||||
// TODO: Consider an option to turn off all erosion as well, or even provide altitude
|
||||
// directly with a closure.
|
||||
seed_elements: true,
|
||||
world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()),
|
||||
..Default::default()
|
||||
},
|
||||
&pool,
|
||||
);
|
||||
let index = index.as_index_ref();
|
||||
(0..GEN_SIZE)
|
||||
.flat_map(|x| (0..GEN_SIZE).map(move |y| Vec2::new(x, y)))
|
||||
|
@ -3,6 +3,7 @@ use client::{
|
||||
error::{Error as ClientError, NetworkConnectError, NetworkError},
|
||||
Client, ServerInfo,
|
||||
};
|
||||
use common::consts::MIN_RECOMMENDED_TOKIO_THREADS;
|
||||
use crossbeam::channel::{unbounded, Receiver, Sender, TryRecvError};
|
||||
use std::{
|
||||
sync::{
|
||||
@ -62,11 +63,12 @@ impl ClientInit {
|
||||
let cancel2 = Arc::clone(&cancel);
|
||||
|
||||
let runtime = runtime.unwrap_or_else(|| {
|
||||
// TODO: evaluate std::thread::available_concurrency as a num_cpus replacement
|
||||
let cores = num_cpus::get();
|
||||
Arc::new(
|
||||
runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.worker_threads(if cores > 4 { cores - 1 } else { cores })
|
||||
.worker_threads((cores / 4).max(MIN_RECOMMENDED_TOKIO_THREADS))
|
||||
.thread_name_fn(|| {
|
||||
static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst);
|
||||
|
@ -1,4 +1,4 @@
|
||||
use common::clock::Clock;
|
||||
use common::{clock::Clock, consts::MIN_RECOMMENDED_TOKIO_THREADS};
|
||||
use crossbeam::channel::{bounded, unbounded, Receiver, Sender, TryRecvError};
|
||||
use server::{
|
||||
persistence::{DatabaseSettings, SqlLogMode},
|
||||
@ -82,12 +82,13 @@ impl Singleplayer {
|
||||
let settings = server::Settings::singleplayer(&server_data_dir);
|
||||
let editable_settings = server::EditableSettings::singleplayer(&server_data_dir);
|
||||
|
||||
// TODO: evaluate std::thread::available_concurrency as a num_cpus replacement
|
||||
let cores = num_cpus::get();
|
||||
debug!("Creating a new runtime for server");
|
||||
let runtime = Arc::new(
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.worker_threads(if cores > 4 { cores - 1 } else { cores })
|
||||
.worker_threads((cores / 4).max(MIN_RECOMMENDED_TOKIO_THREADS))
|
||||
.thread_name_fn(|| {
|
||||
static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst);
|
||||
|
@ -6,10 +6,15 @@ const W: usize = 640;
|
||||
const H: usize = 480;
|
||||
|
||||
fn main() {
|
||||
let (world, index) = World::generate(0, WorldOpts {
|
||||
seed_elements: true,
|
||||
..WorldOpts::default()
|
||||
});
|
||||
let threadpool = rayon::ThreadPoolBuilder::new().build().unwrap();
|
||||
let (world, index) = World::generate(
|
||||
0,
|
||||
WorldOpts {
|
||||
seed_elements: true,
|
||||
..WorldOpts::default()
|
||||
},
|
||||
&threadpool,
|
||||
);
|
||||
|
||||
let index = index.as_index_ref();
|
||||
|
||||
|
@ -29,6 +29,7 @@ fn main() {
|
||||
.with_max_level(Level::ERROR)
|
||||
.with_env_filter(EnvFilter::from_default_env().add_directive(LevelFilter::INFO.into()))
|
||||
.init();
|
||||
let threadpool = rayon::ThreadPoolBuilder::new().build().unwrap();
|
||||
|
||||
// To load a map file of your choice, replace map_file with the name of your map
|
||||
// (stored locally in the map directory of your Veloren root), and swap the
|
||||
@ -42,13 +43,17 @@ fn main() {
|
||||
let mut _map_file = PathBuf::from("./maps");
|
||||
_map_file.push(map_file);
|
||||
|
||||
let (world, index) = World::generate(5284, WorldOpts {
|
||||
seed_elements: false,
|
||||
world_file: sim::FileOpts::LoadAsset(veloren_world::sim::DEFAULT_WORLD_MAP.into()),
|
||||
// world_file: sim::FileOpts::Load(_map_file),
|
||||
// world_file: sim::FileOpts::Save,
|
||||
..WorldOpts::default()
|
||||
});
|
||||
let (world, index) = World::generate(
|
||||
5284,
|
||||
WorldOpts {
|
||||
seed_elements: false,
|
||||
world_file: sim::FileOpts::LoadAsset(veloren_world::sim::DEFAULT_WORLD_MAP.into()),
|
||||
// world_file: sim::FileOpts::Load(_map_file),
|
||||
// world_file: sim::FileOpts::Save,
|
||||
..WorldOpts::default()
|
||||
},
|
||||
&threadpool,
|
||||
);
|
||||
let index = index.as_index_ref();
|
||||
tracing::info!("Sampling data...");
|
||||
let sampler = world.sim();
|
||||
|
138
world/src/lib.rs
138
world/src/lib.rs
@ -87,16 +87,24 @@ impl assets::Asset for Colors {
|
||||
}
|
||||
|
||||
impl World {
|
||||
pub fn generate(seed: u32, opts: sim::WorldOpts) -> (Self, IndexOwned) {
|
||||
pub fn generate(
|
||||
seed: u32,
|
||||
opts: sim::WorldOpts,
|
||||
threadpool: &rayon::ThreadPool,
|
||||
) -> (Self, IndexOwned) {
|
||||
// NOTE: Generating index first in order to quickly fail if the color manifest
|
||||
// is broken.
|
||||
let mut index = Index::new(seed);
|
||||
let mut sim = sim::WorldSim::generate(seed, opts);
|
||||
let civs = civ::Civs::generate(seed, &mut sim, &mut index);
|
||||
threadpool.install(|| {
|
||||
let mut index = Index::new(seed);
|
||||
|
||||
sim2::simulate(&mut index, &mut sim);
|
||||
let mut sim = sim::WorldSim::generate(seed, opts, threadpool);
|
||||
|
||||
(Self { sim, civs }, IndexOwned::new(index))
|
||||
let civs = civ::Civs::generate(seed, &mut sim, &mut index);
|
||||
|
||||
sim2::simulate(&mut index, &mut sim);
|
||||
|
||||
(Self { sim, civs }, IndexOwned::new(index))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn sim(&self) -> &sim::WorldSim { &self.sim }
|
||||
@ -107,66 +115,68 @@ impl World {
|
||||
// TODO
|
||||
}
|
||||
|
||||
pub fn get_map_data(&self, index: IndexRef) -> WorldMapMsg {
|
||||
// we need these numbers to create unique ids for cave ends
|
||||
let num_sites = self.civs().sites().count() as u64;
|
||||
let num_caves = self.civs().caves.values().count() as u64;
|
||||
WorldMapMsg {
|
||||
pois: self.civs().pois.iter().map(|(_, poi)| {
|
||||
world_msg::PoiInfo {
|
||||
name: poi.name.clone(),
|
||||
kind: match &poi.kind {
|
||||
civ::PoiKind::Peak(alt) => world_msg::PoiKind::Peak(*alt),
|
||||
civ::PoiKind::Lake(size) => world_msg::PoiKind::Lake(*size),
|
||||
},
|
||||
wpos: poi.loc * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||
}
|
||||
}).collect(),
|
||||
sites: self
|
||||
.civs()
|
||||
.sites
|
||||
.iter()
|
||||
.map(|(_, site)| {
|
||||
world_msg::SiteInfo {
|
||||
id: site.site_tmp.map(|i| i.id()).unwrap_or_default(),
|
||||
name: site.site_tmp.map(|id| index.sites[id].name().to_string()),
|
||||
// TODO: Probably unify these, at some point
|
||||
kind: match &site.kind {
|
||||
civ::SiteKind::Settlement => world_msg::SiteKind::Town,
|
||||
civ::SiteKind::Dungeon => world_msg::SiteKind::Dungeon {
|
||||
difficulty: match site.site_tmp.map(|id| &index.sites[id].kind) {
|
||||
Some(site::SiteKind::Dungeon(d)) => d.difficulty(),
|
||||
_ => 0,
|
||||
},
|
||||
},
|
||||
civ::SiteKind::Castle => world_msg::SiteKind::Castle,
|
||||
civ::SiteKind::Refactor => world_msg::SiteKind::Town,
|
||||
civ::SiteKind::Tree => world_msg::SiteKind::Tree,
|
||||
pub fn get_map_data(&self, index: IndexRef, threadpool: &rayon::ThreadPool) -> WorldMapMsg {
|
||||
threadpool.install(|| {
|
||||
// we need these numbers to create unique ids for cave ends
|
||||
let num_sites = self.civs().sites().count() as u64;
|
||||
let num_caves = self.civs().caves.values().count() as u64;
|
||||
WorldMapMsg {
|
||||
pois: self.civs().pois.iter().map(|(_, poi)| {
|
||||
world_msg::PoiInfo {
|
||||
name: poi.name.clone(),
|
||||
kind: match &poi.kind {
|
||||
civ::PoiKind::Peak(alt) => world_msg::PoiKind::Peak(*alt),
|
||||
civ::PoiKind::Lake(size) => world_msg::PoiKind::Lake(*size),
|
||||
},
|
||||
wpos: site.center * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||
wpos: poi.loc * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||
}
|
||||
})
|
||||
.chain(
|
||||
self.civs()
|
||||
.caves
|
||||
.iter()
|
||||
.map(|(id, info)| {
|
||||
// separate the two locations, combine with name
|
||||
std::iter::once((id.id()+num_sites, info.name.clone(), info.location.0))
|
||||
// unfortunately we have to introduce a fake id (as it gets stored in a map in the client)
|
||||
.chain(std::iter::once((id.id()+num_sites+num_caves, info.name.clone(), info.location.1)))
|
||||
})
|
||||
.flatten() // unwrap inner iteration
|
||||
.map(|(id, name, pos)| world_msg::SiteInfo {
|
||||
id,
|
||||
name: Some(name),
|
||||
kind: world_msg::SiteKind::Cave,
|
||||
wpos: pos,
|
||||
}),
|
||||
)
|
||||
.collect(),
|
||||
..self.sim.get_map(index)
|
||||
}
|
||||
}).collect(),
|
||||
sites: self
|
||||
.civs()
|
||||
.sites
|
||||
.iter()
|
||||
.map(|(_, site)| {
|
||||
world_msg::SiteInfo {
|
||||
id: site.site_tmp.map(|i| i.id()).unwrap_or_default(),
|
||||
name: site.site_tmp.map(|id| index.sites[id].name().to_string()),
|
||||
// TODO: Probably unify these, at some point
|
||||
kind: match &site.kind {
|
||||
civ::SiteKind::Settlement => world_msg::SiteKind::Town,
|
||||
civ::SiteKind::Dungeon => world_msg::SiteKind::Dungeon {
|
||||
difficulty: match site.site_tmp.map(|id| &index.sites[id].kind) {
|
||||
Some(site::SiteKind::Dungeon(d)) => d.difficulty(),
|
||||
_ => 0,
|
||||
},
|
||||
},
|
||||
civ::SiteKind::Castle => world_msg::SiteKind::Castle,
|
||||
civ::SiteKind::Refactor => world_msg::SiteKind::Town,
|
||||
civ::SiteKind::Tree => world_msg::SiteKind::Tree,
|
||||
},
|
||||
wpos: site.center * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||
}
|
||||
})
|
||||
.chain(
|
||||
self.civs()
|
||||
.caves
|
||||
.iter()
|
||||
.map(|(id, info)| {
|
||||
// separate the two locations, combine with name
|
||||
std::iter::once((id.id() + num_sites, info.name.clone(), info.location.0))
|
||||
// unfortunately we have to introduce a fake id (as it gets stored in a map in the client)
|
||||
.chain(std::iter::once((id.id() + num_sites + num_caves, info.name.clone(), info.location.1)))
|
||||
})
|
||||
.flatten() // unwrap inner iteration
|
||||
.map(|(id, name, pos)| world_msg::SiteInfo {
|
||||
id,
|
||||
name: Some(name),
|
||||
kind: world_msg::SiteKind::Cave,
|
||||
wpos: pos,
|
||||
}),
|
||||
)
|
||||
.collect(),
|
||||
..self.sim.get_map(index)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn sample_columns(
|
||||
|
@ -28,6 +28,7 @@ pub type Alt = f64;
|
||||
pub type Compute = f64;
|
||||
pub type Computex8 = [Compute; 8];
|
||||
|
||||
/* code used by sharp in future
|
||||
/// Compute the water flux at all chunks, given a list of chunk indices sorted
|
||||
/// by increasing height.
|
||||
pub fn get_drainage(
|
||||
@ -56,6 +57,7 @@ pub fn get_drainage(
|
||||
});
|
||||
flux
|
||||
}
|
||||
*/
|
||||
|
||||
/// Compute the water flux at all chunks for multiple receivers, given a list of
|
||||
/// chunk indices sorted by increasing height and weights for each receiver.
|
||||
@ -710,6 +712,7 @@ fn erode(
|
||||
// scaling factors
|
||||
height_scale: impl Fn(f32) -> Alt + Sync,
|
||||
k_da_scale: impl Fn(f64) -> f64,
|
||||
threadpool: &rayon::ThreadPool,
|
||||
) {
|
||||
let compute_stats = true;
|
||||
debug!("Done draining...");
|
||||
@ -795,7 +798,7 @@ fn erode(
|
||||
let k_fs_mult_sed = 4.0;
|
||||
// Dimensionless multiplier for G when land becomes sediment.
|
||||
let g_fs_mult_sed = 1.0;
|
||||
let ((dh, newh, maxh, mrec, mstack, mwrec, area), (mut max_slopes, h_t)) = rayon::join(
|
||||
let ((dh, newh, maxh, mrec, mstack, mwrec, area), (mut max_slopes, h_t)) = threadpool.join(
|
||||
|| {
|
||||
let mut dh = downhill(
|
||||
map_size_lg,
|
||||
@ -817,6 +820,7 @@ fn erode(
|
||||
dx as Compute,
|
||||
dy as Compute,
|
||||
maxh,
|
||||
threadpool,
|
||||
);
|
||||
debug!("Got multiple receivers...");
|
||||
// TODO: Figure out how to switch between single-receiver and multi-receiver
|
||||
@ -827,7 +831,7 @@ fn erode(
|
||||
(dh, newh, maxh, mrec, mstack, mwrec, area)
|
||||
},
|
||||
|| {
|
||||
rayon::join(
|
||||
threadpool.join(
|
||||
|| {
|
||||
let max_slope = get_max_slope(map_size_lg, h, rock_strength_nz, |posi| {
|
||||
height_scale(n_f(posi))
|
||||
@ -851,7 +855,7 @@ fn erode(
|
||||
|
||||
// Precompute factors for Stream Power Law.
|
||||
let czero = <SimdType as Zero>::zero();
|
||||
let (k_fs_fact, k_df_fact) = rayon::join(
|
||||
let (k_fs_fact, k_df_fact) = threadpool.join(
|
||||
|| {
|
||||
dh.par_iter()
|
||||
.enumerate()
|
||||
@ -1081,7 +1085,7 @@ fn erode(
|
||||
// Keep track of how many iterations we've gone to test for convergence.
|
||||
n_gs_stream_power_law += 1;
|
||||
|
||||
rayon::join(
|
||||
threadpool.join(
|
||||
|| {
|
||||
// guess/update the elevation at t+Δt (k)
|
||||
(&mut *h_p, &*h_stack)
|
||||
@ -1721,7 +1725,7 @@ fn erode(
|
||||
/// http://horizon.documentation.ird.fr/exl-doc/pleins_textes/pleins_textes_7/sous_copyright/010031925.pdf
|
||||
///
|
||||
/// See https://github.com/mewo2/terrain/blob/master/terrain.js
|
||||
pub fn fill_sinks<F: Float + Send + Sync>(
|
||||
pub(crate) fn fill_sinks<F: Float + Send + Sync>(
|
||||
map_size_lg: MapSizeLg,
|
||||
h: impl Fn(usize) -> F + Sync,
|
||||
is_ocean: impl Fn(usize) -> bool + Sync,
|
||||
@ -2339,6 +2343,7 @@ pub fn get_multi_rec<F: fmt::Debug + Float + Sync + Into<Compute>>(
|
||||
dx: Compute,
|
||||
dy: Compute,
|
||||
_maxh: F,
|
||||
threadpool: &rayon::ThreadPool,
|
||||
) -> (Box<[u8]>, Box<[u32]>, Box<[Computex8]>) {
|
||||
let nn = nx * ny;
|
||||
let dxdy = Vec2::new(dx, dy);
|
||||
@ -2437,7 +2442,7 @@ pub fn get_multi_rec<F: fmt::Debug + Float + Sync + Into<Compute>>(
|
||||
.unzip_into_vecs(&mut mrec, &mut don_vis);
|
||||
|
||||
let czero = <Compute as Zero>::zero();
|
||||
let (wrec, stack) = rayon::join(
|
||||
let (wrec, stack) = threadpool.join(
|
||||
|| {
|
||||
(0..nn)
|
||||
.into_par_iter()
|
||||
@ -2541,6 +2546,7 @@ pub fn do_erosion(
|
||||
height_scale: impl Fn(f32) -> Alt + Sync,
|
||||
k_d_scale: f64,
|
||||
k_da_scale: impl Fn(f64) -> f64,
|
||||
threadpool: &rayon::ThreadPool,
|
||||
) -> (Box<[Alt]>, Box<[Alt]> /* , Box<[Alt]> */) {
|
||||
debug!("Initializing erosion arrays...");
|
||||
let oldh_ = (0..map_size_lg.chunks_len())
|
||||
@ -2663,6 +2669,7 @@ pub fn do_erosion(
|
||||
|posi| is_ocean(posi),
|
||||
height_scale,
|
||||
k_da_scale,
|
||||
threadpool,
|
||||
);
|
||||
});
|
||||
(h, b)
|
||||
|
@ -9,18 +9,21 @@ mod way;
|
||||
use self::erosion::Compute;
|
||||
pub use self::{
|
||||
diffusion::diffusion,
|
||||
erosion::{
|
||||
do_erosion, fill_sinks, get_drainage, get_lakes, get_multi_drainage, get_multi_rec,
|
||||
get_rivers, mrec_downhill, Alt, RiverData, RiverKind,
|
||||
},
|
||||
location::Location,
|
||||
map::{sample_pos, sample_wpos},
|
||||
util::{
|
||||
cdf_irwin_hall, downhill, get_horizon_map, get_oceans, local_cells, map_edge_factor,
|
||||
uniform_noise, uphill, InverseCdf, ScaleBias,
|
||||
},
|
||||
util::get_horizon_map,
|
||||
way::{Cave, Path, Way},
|
||||
};
|
||||
pub(crate) use self::{
|
||||
erosion::{
|
||||
do_erosion, fill_sinks, get_lakes, get_multi_drainage, get_multi_rec, get_rivers, Alt,
|
||||
RiverData, RiverKind,
|
||||
},
|
||||
util::{
|
||||
cdf_irwin_hall, downhill, get_oceans, local_cells, map_edge_factor, uniform_noise, uphill,
|
||||
InverseCdf,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
all::{Environment, ForestKind, TreeAttr},
|
||||
@ -368,7 +371,7 @@ pub struct WorldSim {
|
||||
impl WorldSim {
|
||||
#[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587
|
||||
|
||||
pub fn generate(seed: u32, opts: WorldOpts) -> Self {
|
||||
pub fn generate(seed: u32, opts: WorldOpts, threadpool: &rayon::ThreadPool) -> Self {
|
||||
// Parse out the contents of various map formats into the values we need.
|
||||
let parsed_world_file = (|| {
|
||||
let map = match opts.world_file {
|
||||
@ -619,7 +622,7 @@ impl WorldSim {
|
||||
|
||||
// No NaNs in these uniform vectors, since the original noise value always
|
||||
// returns Some.
|
||||
let ((alt_base, _), (chaos, _)) = rayon::join(
|
||||
let ((alt_base, _), (chaos, _)) = threadpool.join(
|
||||
|| {
|
||||
uniform_noise(map_size_lg, |_, wposf| {
|
||||
// "Base" of the chunk, to be multiplied by CONFIG.mountain_scale (multiplied
|
||||
@ -1067,6 +1070,7 @@ impl WorldSim {
|
||||
height_scale,
|
||||
k_d_scale(n_approx),
|
||||
k_da_scale,
|
||||
threadpool,
|
||||
);
|
||||
|
||||
// Quick "small scale" erosion cycle in order to lower extreme angles.
|
||||
@ -1090,6 +1094,7 @@ impl WorldSim {
|
||||
height_scale,
|
||||
k_d_scale(n_approx),
|
||||
k_da_scale,
|
||||
threadpool,
|
||||
)
|
||||
};
|
||||
|
||||
@ -1168,6 +1173,7 @@ impl WorldSim {
|
||||
height_scale,
|
||||
k_d_scale(n_approx),
|
||||
k_da_scale,
|
||||
threadpool,
|
||||
)
|
||||
};
|
||||
|
||||
@ -1190,6 +1196,7 @@ impl WorldSim {
|
||||
TerrainChunkSize::RECT_SIZE.x as Compute,
|
||||
TerrainChunkSize::RECT_SIZE.y as Compute,
|
||||
maxh,
|
||||
threadpool,
|
||||
)
|
||||
};
|
||||
let flux_old = get_multi_drainage(map_size_lg, &mstack, &mrec, &*mwrec, boundary_len);
|
||||
@ -1318,61 +1325,63 @@ impl WorldSim {
|
||||
};
|
||||
|
||||
// NaNs in these uniform vectors wherever pure_water() returns true.
|
||||
let (((alt_no_water, _), (pure_flux, _)), ((temp_base, _), (humid_base, _))) = rayon::join(
|
||||
|| {
|
||||
rayon::join(
|
||||
|| {
|
||||
uniform_noise(map_size_lg, |posi, _| {
|
||||
if pure_water(posi) {
|
||||
None
|
||||
} else {
|
||||
// A version of alt that is uniform over *non-water* (or
|
||||
// land-adjacent water) chunks.
|
||||
Some(alt[posi] as f32)
|
||||
}
|
||||
})
|
||||
},
|
||||
|| {
|
||||
uniform_noise(map_size_lg, |posi, _| {
|
||||
if pure_water(posi) {
|
||||
None
|
||||
} else {
|
||||
Some(flux_old[posi])
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
},
|
||||
|| {
|
||||
rayon::join(
|
||||
|| {
|
||||
uniform_noise(map_size_lg, |posi, wposf| {
|
||||
if pure_water(posi) {
|
||||
None
|
||||
} else {
|
||||
// -1 to 1.
|
||||
Some(gen_ctx.temp_nz.get((wposf).into_array()) as f32)
|
||||
}
|
||||
})
|
||||
},
|
||||
|| {
|
||||
uniform_noise(map_size_lg, |posi, wposf| {
|
||||
// Check whether any tiles around this tile are water.
|
||||
if pure_water(posi) {
|
||||
None
|
||||
} else {
|
||||
// 0 to 1, hopefully.
|
||||
Some(
|
||||
(gen_ctx.humid_nz.get(wposf.div(1024.0).into_array()) as f32)
|
||||
.add(1.0)
|
||||
.mul(0.5),
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
},
|
||||
);
|
||||
let (((alt_no_water, _), (pure_flux, _)), ((temp_base, _), (humid_base, _))) = threadpool
|
||||
.join(
|
||||
|| {
|
||||
threadpool.join(
|
||||
|| {
|
||||
uniform_noise(map_size_lg, |posi, _| {
|
||||
if pure_water(posi) {
|
||||
None
|
||||
} else {
|
||||
// A version of alt that is uniform over *non-water* (or
|
||||
// land-adjacent water) chunks.
|
||||
Some(alt[posi] as f32)
|
||||
}
|
||||
})
|
||||
},
|
||||
|| {
|
||||
uniform_noise(map_size_lg, |posi, _| {
|
||||
if pure_water(posi) {
|
||||
None
|
||||
} else {
|
||||
Some(flux_old[posi])
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
},
|
||||
|| {
|
||||
threadpool.join(
|
||||
|| {
|
||||
uniform_noise(map_size_lg, |posi, wposf| {
|
||||
if pure_water(posi) {
|
||||
None
|
||||
} else {
|
||||
// -1 to 1.
|
||||
Some(gen_ctx.temp_nz.get((wposf).into_array()) as f32)
|
||||
}
|
||||
})
|
||||
},
|
||||
|| {
|
||||
uniform_noise(map_size_lg, |posi, wposf| {
|
||||
// Check whether any tiles around this tile are water.
|
||||
if pure_water(posi) {
|
||||
None
|
||||
} else {
|
||||
// 0 to 1, hopefully.
|
||||
Some(
|
||||
(gen_ctx.humid_nz.get(wposf.div(1024.0).into_array())
|
||||
as f32)
|
||||
.add(1.0)
|
||||
.mul(0.5),
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
let gen_cdf = GenCdf {
|
||||
humid_base,
|
||||
|
@ -771,6 +771,7 @@ impl NoiseFn<[f64; 4]> for HybridMulti {
|
||||
}
|
||||
}
|
||||
|
||||
/* code used by sharp in future
|
||||
/// Noise function that applies a scaling factor and a bias to the output value
|
||||
/// from the source function.
|
||||
///
|
||||
@ -810,3 +811,4 @@ impl<'a, F: NoiseFn<T> + 'a, T> NoiseFn<T> for ScaleBias<'a, F> {
|
||||
#[cfg(target_os = "emscripten")]
|
||||
fn get(&self, point: T) -> f64 { (self.source.get(point) * self.scale) + self.bias }
|
||||
}
|
||||
*/
|
||||
|
@ -1071,6 +1071,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_economy() {
|
||||
init();
|
||||
let threadpool = rayon::ThreadPoolBuilder::new().build().unwrap();
|
||||
info!("init");
|
||||
let seed = 59686;
|
||||
let opts = sim::WorldOpts {
|
||||
@ -1080,7 +1081,7 @@ mod tests {
|
||||
};
|
||||
let mut index = crate::index::Index::new(seed);
|
||||
info!("Index created");
|
||||
let mut sim = sim::WorldSim::generate(seed, opts);
|
||||
let mut sim = sim::WorldSim::generate(seed, opts, &threadpool);
|
||||
info!("World loaded");
|
||||
let regenerate_input = false;
|
||||
if regenerate_input {
|
||||
|
Loading…
Reference in New Issue
Block a user