mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Small fix to executor scheduling.
Also some preliminary separation for new lighting and removal of some unneeded checks.
This commit is contained in:
parent
8d9680c68b
commit
0c99438993
@ -13,7 +13,7 @@ use executors::{
|
||||
parker::{LargeThreadData, StaticParker},
|
||||
Executor,
|
||||
};
|
||||
pub use executors::{builder::ThreadPoolBuilder, parker::large};
|
||||
pub use executors::{builder::ThreadPoolBuilder, parker::large, FuturesExecutor};
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use pin_project_lite::pin_project;
|
||||
// use rayon::ThreadPool;
|
||||
@ -515,7 +515,7 @@ impl SlowJobPool {
|
||||
|
||||
// NOTE: It's important not to use internal until we're in the spawned thread, since the
|
||||
// lock is probably currently taken!
|
||||
self.threadpool.execute(move || {
|
||||
self.threadpool./*execute*/spawn(async move {
|
||||
// Repeatedly run until exit; we do things this way to avoid recursion, which might blow
|
||||
// our call stack.
|
||||
loop {
|
||||
@ -549,6 +549,19 @@ impl SlowJobPool {
|
||||
execution_start,
|
||||
execution_end,
|
||||
};
|
||||
// Hint to the scheduler to run any tasks that were explicitly woken. This should
|
||||
// run explicitly-woken tasks first (if enough time has elapsed, including tasks
|
||||
// woken by other threads). Waiting to acquire the next job until we've yielded
|
||||
// also gives other threads the opportunity to take any queued-up slowjobs.
|
||||
tokio::task::yield_now().await;
|
||||
// Hint to scheduler to run any other threads that are currently waiting, since
|
||||
// slowjobs are, well, slow! i.e. the cost of yielding should be very small
|
||||
// compared to the cost of actually running the job for *almost* every job on the
|
||||
// pool (and for jobs for which this isn't true, we should probably batch them up).
|
||||
//
|
||||
// NOTE: Ideally this would be done automatically as part of the yield, but
|
||||
// async_executor currently isn't a very good citizen in this regard.
|
||||
std::thread::yield_now();
|
||||
// directly maintain the next task afterwards
|
||||
let next_task = {
|
||||
// We take the lock in this scope to make sure it's dropped before we
|
||||
@ -569,13 +582,8 @@ impl SlowJobPool {
|
||||
// times or something in case we have more tasks to execute).
|
||||
return;
|
||||
};
|
||||
// Hint to scheduler to run any other threads that are currently waiting, since
|
||||
// slowjobs are, well, slow :) i.e. the cost of yielding should be very small
|
||||
// compared to the cost of actually running the job for *almost* every job on the
|
||||
// pool (and for jobs for which this isn't true, we should probably batch them up).
|
||||
std::thread::yield_now();
|
||||
}
|
||||
});
|
||||
}).detach();
|
||||
}
|
||||
|
||||
/// spawn a new slow job on a certain NAME IF it can run immediately
|
||||
|
@ -71,8 +71,57 @@ fn flat_get<'a>(flat: &'a Vec<Block>, w: i32, h: i32, d: i32) -> impl Fn(Vec3<i3
|
||||
} */
|
||||
}
|
||||
|
||||
const UNKNOWN: u8 = 255;
|
||||
|
||||
struct LightMap {
|
||||
default_light: u8,
|
||||
bounds: Aabb<i32>,
|
||||
light_map: Vec<u8>,
|
||||
}
|
||||
|
||||
impl LightMap {
|
||||
fn into_fn(self) -> impl Fn(Vec3<i32>) -> f32 + 'static + Send + Sync {
|
||||
let outer = Aabb {
|
||||
min: self.bounds.min/* - Vec3::new(SUNLIGHT as i32, SUNLIGHT as i32, 1) */ - Vec3::new(0, 0, 1),
|
||||
max: self.bounds.max/* + Vec3::new(SUNLIGHT as i32, SUNLIGHT as i32, 1) */ + Vec3::new(0, 0, 1),
|
||||
};
|
||||
|
||||
/* let mut vol_cached = vol.cached(); */
|
||||
|
||||
let mut light_map_ = vec![UNKNOWN; outer.size().product() as usize];
|
||||
let (w_, h_, d_) = outer.clone().size().into_tuple();
|
||||
let wh_ = w_ * h_;
|
||||
|
||||
let lm_idx = {
|
||||
#[inline(always)] move |x, y, z| {
|
||||
(wh_ * z + w_ * y + x) as usize
|
||||
}
|
||||
};
|
||||
|
||||
let min_bounds = Aabb {
|
||||
min: self.bounds.min - Vec3::unit_z(),
|
||||
max: self.bounds.max + Vec3::unit_z(),
|
||||
};
|
||||
|
||||
#[inline(always)] move |wpos| {
|
||||
/* if is_sunlight { return 1.0 } else { 0.0 } */
|
||||
let pos = wpos - min_bounds.min;
|
||||
let l = self.light_map
|
||||
.get(/*lm_idx2*/lm_idx(pos.x, pos.y, pos.z))
|
||||
.copied()
|
||||
.unwrap_or(if pos.z < 0 { 0 } else { self.default_light });
|
||||
|
||||
if /* l != OPAQUE && */l != UNKNOWN {
|
||||
l as f32 * SUNLIGHT_INV
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn calc_light<'a,
|
||||
V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static,
|
||||
/* V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static, */
|
||||
I: Iterator<Item=(Vec3<i32>, u8)>,
|
||||
/* F: /*for<'x> */for<'a> fn(&'a Vec<Block>) -> G, */
|
||||
/* G: /*[&'x &'a (); 0], */Fn(Vec3<i32>) -> Block, */
|
||||
@ -82,13 +131,12 @@ fn calc_light<'a,
|
||||
default_light: u8,
|
||||
bounds: Aabb<i32>,
|
||||
range: Aabb<i32>,
|
||||
vol: &'a VolGrid2d<V>,
|
||||
/* vol: &'a VolGrid2d<V>, */
|
||||
lit_blocks: I,
|
||||
flat: &'a Vec<Block>,
|
||||
(w, h, d): (i32, i32, i32)
|
||||
) -> impl Fn(Vec3<i32>) -> f32 + 'static + Send + Sync/*CalcLightFn*//*<V, I>*/ {
|
||||
) -> /*impl Fn(Vec3<i32>) -> f32 + 'static + Send + Sync/*CalcLightFn*//*<V, I>*/*/LightMap {
|
||||
span!(_guard, "calc_light");
|
||||
const UNKNOWN: u8 = 255;
|
||||
const OPAQUE: u8 = 254;
|
||||
|
||||
let outer = Aabb {
|
||||
@ -295,7 +343,7 @@ fn calc_light<'a,
|
||||
});
|
||||
});
|
||||
|
||||
let min_bounds = Aabb {
|
||||
/* let min_bounds = Aabb {
|
||||
min: bounds.min - Vec3::unit_z(),
|
||||
max: bounds.max + Vec3::unit_z(),
|
||||
};
|
||||
@ -332,31 +380,47 @@ fn calc_light<'a,
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
} */
|
||||
LightMap {
|
||||
default_light,
|
||||
bounds,
|
||||
light_map: light_map_,
|
||||
}
|
||||
}
|
||||
|
||||
type V = TerrainChunk;
|
||||
|
||||
#[tracing::instrument(skip_all, name = "<&VolGrid2d as Meshable<_, _>>::generate_mesh")]
|
||||
pub struct PreparedLights {
|
||||
flat: Vec<Block>,
|
||||
row_kinds: Vec<u8>,
|
||||
z_start: i32,
|
||||
z_end: i32,
|
||||
light: LightMap,
|
||||
glow: LightMap,
|
||||
}
|
||||
|
||||
/// Representative block for air.
|
||||
const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty);
|
||||
/// Representative block for liquid.
|
||||
///
|
||||
/// FIXME: Can you really skip meshing for general liquids? Probably not...
|
||||
const LIQUID: Block = Block::water(common::terrain::sprite::SpriteKind::Empty);
|
||||
/// Representtive block for solids.
|
||||
///
|
||||
/// FIXME: Really hacky!
|
||||
const OPAQUE: Block = Block::lava(common::terrain::sprite::SpriteKind::Empty);
|
||||
|
||||
const ALL_OPAQUE: u8 = 0b1;
|
||||
const ALL_LIQUID: u8 = 0b10;
|
||||
const ALL_AIR: u8 = 0b100;
|
||||
|
||||
#[tracing::instrument(skip_all, name = "<&VolGrid2d as Meshable<_, _>>::prepare_lights")]
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[inline(always)]
|
||||
pub async fn generate_mesh<'a, F: Future<Output=Option<Model<[u8; 4]>>> + 'a>(
|
||||
vol: &'a VolGrid2d<V>,
|
||||
create_texture: impl FnOnce(usize) -> /*Option<Model<[u8; 4]>>*/F + Send,
|
||||
(range, max_texture_size, boi): (Aabb<i32>, Vec2<u16>, &'a BlocksOfInterest),
|
||||
) -> MeshGen<
|
||||
TerrainVertex,
|
||||
FluidVertex,
|
||||
TerrainVertex,
|
||||
(
|
||||
Aabb<f32>,
|
||||
/*ColLightInfo*/(Option<Model<[u8; 4]>>, Vec2<u16>),
|
||||
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
|
||||
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
|
||||
AltIndices,
|
||||
(f32, f32),
|
||||
),
|
||||
> {
|
||||
pub fn prepare_lights(
|
||||
vol: &VolGrid2d<V>,
|
||||
(range, boi): (Aabb<i32>, &BlocksOfInterest),
|
||||
) -> PreparedLights {
|
||||
/* span!(
|
||||
_guard,
|
||||
"generate_mesh",
|
||||
@ -374,20 +438,6 @@ pub async fn generate_mesh<'a, F: Future<Output=Option<Model<[u8; 4]>>> + 'a>(
|
||||
// z can range from -1..range.size().d + 1
|
||||
let d = d + 2;
|
||||
|
||||
/// Representative block for air.
|
||||
const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty);
|
||||
/// Representative block for liquid.
|
||||
///
|
||||
/// FIXME: Can you really skip meshing for general liquids? Probably not...
|
||||
const LIQUID: Block = Block::water(common::terrain::sprite::SpriteKind::Empty);
|
||||
/// Representtive block for solids.
|
||||
///
|
||||
/// FIXME: Really hacky!
|
||||
const OPAQUE: Block = Block::lava(common::terrain::sprite::SpriteKind::Empty);
|
||||
|
||||
const ALL_OPAQUE: u8 = 0b1;
|
||||
const ALL_LIQUID: u8 = 0b10;
|
||||
const ALL_AIR: u8 = 0b100;
|
||||
// For each horizontal slice of the chunk, we keep track of what kinds of blocks are in it.
|
||||
// This allows us to compute limits after the fact, much more precisely than keeping track of a
|
||||
// single intersection would; it also lets us skip homogeoneous slices entirely.
|
||||
@ -652,6 +702,7 @@ pub async fn generate_mesh<'a, F: Future<Output=Option<Model<[u8; 4]>>> + 'a>(
|
||||
let z_max = chonk.get_max_z() - chonk.get_min_z();
|
||||
let below = *chonk.below();
|
||||
|
||||
// FIXME: This copies way more groups than necessary for side and corner chunks.
|
||||
let flat_chunk = chonk.make_flat(&arena);
|
||||
|
||||
let min_z_ = z_diff - intersection.min.z;
|
||||
@ -665,6 +716,8 @@ pub async fn generate_mesh<'a, F: Future<Output=Option<Model<[u8; 4]>>> + 'a>(
|
||||
ALL_AIR
|
||||
};
|
||||
|
||||
// chonk.min.z - flat_range.min.z = chonk.min.z - (range.min.z - 1)
|
||||
// NOTE: max(0) should not be required when this is fixed to work properly.
|
||||
let skip_count = min_z_.max(0);
|
||||
let take_count = (max_z_.min(d) - skip_count).max(0);
|
||||
let skip_count = skip_count as usize;
|
||||
@ -793,7 +846,7 @@ pub async fn generate_mesh<'a, F: Future<Output=Option<Model<[u8; 4]>>> + 'a>(
|
||||
light_end = Some(z);
|
||||
}
|
||||
},
|
||||
// We could probably handle mixed opaque/liquid a well, since it's pretty much guaranteed
|
||||
// We could probably handle mixed opaque/liquid as well, since it's pretty much guaranteed
|
||||
// to attenuate, but we choose not to since we expect this to be much more
|
||||
// common than fully opaque chunks.
|
||||
_ => {},
|
||||
@ -896,8 +949,54 @@ pub async fn generate_mesh<'a, F: Future<Output=Option<Model<[u8; 4]>>> + 'a>(
|
||||
}
|
||||
}
|
||||
} */
|
||||
let light = calc_light(true, SUNLIGHT, light_range, range, vol, core::iter::empty(), &flat, (w, h, d));
|
||||
let glow = calc_light(false, 0, glow_range, range, vol, glow_blocks.into_iter(), &flat, (w, h, d));
|
||||
let light = calc_light(true, SUNLIGHT, light_range, range/*, vol*/, core::iter::empty(), &flat, (w, h, d));
|
||||
let glow = calc_light(false, 0, glow_range, range/*, vol*/, glow_blocks.into_iter(), &flat, (w, h, d));
|
||||
|
||||
PreparedLights {
|
||||
flat,
|
||||
row_kinds,
|
||||
z_start,
|
||||
z_end,
|
||||
light,
|
||||
glow,
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, name = "<&VolGrid2d as Meshable<_, _>>::generate_mesh")]
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[inline(always)]
|
||||
pub async fn generate_mesh<'a, F: Future<Output=Option<Model<[u8; 4]>>> + 'a>(
|
||||
vol: &'a VolGrid2d<V>,
|
||||
create_texture: impl FnOnce(usize) -> /*Option<Model<[u8; 4]>>*/F + Send,
|
||||
(range, max_texture_size, boi): (Aabb<i32>, Vec2<u16>, &'a BlocksOfInterest),
|
||||
) -> MeshGen<
|
||||
TerrainVertex,
|
||||
FluidVertex,
|
||||
TerrainVertex,
|
||||
(
|
||||
Aabb<f32>,
|
||||
/*ColLightInfo*/(Option<Model<[u8; 4]>>, Vec2<u16>),
|
||||
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
|
||||
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
|
||||
AltIndices,
|
||||
(f32, f32),
|
||||
),
|
||||
> {
|
||||
/* span!(
|
||||
_guard,
|
||||
"generate_mesh",
|
||||
"<&VolGrid2d as Meshable<_, _>>::generate_mesh"
|
||||
); */
|
||||
|
||||
let (w, h, d) = range.size().into_tuple();
|
||||
// z can range from -1..range.size().d + 1
|
||||
let d = d + 2;
|
||||
|
||||
let PreparedLights { flat, row_kinds, z_start, z_end, light, glow } =
|
||||
prepare_lights(vol, (range, boi));
|
||||
|
||||
let light = light.into_fn();
|
||||
let glow = glow.into_fn();
|
||||
|
||||
let max_size = max_texture_size;
|
||||
assert!(z_end >= z_start);
|
||||
@ -924,7 +1023,15 @@ pub async fn generate_mesh<'a, F: Future<Output=Option<Model<[u8; 4]>>> + 'a>(
|
||||
let mut greedy =
|
||||
GreedyMesh::<guillotiere::SimpleAtlasAllocator>::new(max_size, greedy::terrain_config());
|
||||
let greedy_size = Vec3::new(range.size().w - 2, range.size().h - 2, z_end - z_start + 1);
|
||||
// NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5
|
||||
// + 14). FIXME: Make this function fallible, since the terrain
|
||||
// information might be dynamically generated which would make this hard
|
||||
// to enforce.
|
||||
assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384);
|
||||
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
|
||||
// which always fits into a f32.
|
||||
let max_bounds: Vec3<f32> = greedy_size.as_::<f32>();
|
||||
// NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16.
|
||||
let mesh_delta = Vec3::new(0.0, 0.0, (z_start + range.min.z) as f32);
|
||||
let mut opaque_deep = Vec::new();
|
||||
let mut opaque_shallow = Vec::new();
|
||||
@ -938,13 +1045,6 @@ pub async fn generate_mesh<'a, F: Future<Output=Option<Model<[u8; 4]>>> + 'a>(
|
||||
let mut do_draw_greedy = #[inline(always)] |z_start: i32, z_end: i32| {
|
||||
// dbg!(range.min, z_start, z_end);
|
||||
let greedy_size = Vec3::new(range.size().w - 2, range.size().h - 2, z_end - z_start + 1);
|
||||
// NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5
|
||||
// + 14). FIXME: Make this function fallible, since the terrain
|
||||
// information might be dynamically generated which would make this hard
|
||||
// to enforce.
|
||||
assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384);
|
||||
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
|
||||
// which always fits into a f32.
|
||||
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
|
||||
// which always fits into a usize.
|
||||
let greedy_size = greedy_size.as_::<usize>();
|
||||
|
@ -1128,6 +1128,8 @@ impl/*<V: RectRasterableVol>*/ Terrain<V> {
|
||||
for j in -1..2 {
|
||||
let pos = pos + Vec2::new(i, j);
|
||||
|
||||
let is_sender = (pos.x & 1) ^ (pos.y & 1);
|
||||
|
||||
let entry = self.mesh_todo.entry(pos);
|
||||
let done_meshing = self.chunks.contains_key(&pos);
|
||||
let in_progress = done_meshing || matches!(entry, hash_map::Entry::Occupied(_));
|
||||
|
@ -752,7 +752,7 @@ impl Settlement {
|
||||
BlockKind::Grass,
|
||||
BlockKind::Earth,
|
||||
BlockKind::Sand,
|
||||
BlockKind::Snow,
|
||||
/* BlockKind::Snow, */
|
||||
BlockKind::Rock,
|
||||
]
|
||||
.contains(&block.kind())
|
||||
|
@ -1113,10 +1113,10 @@ impl Site {
|
||||
let mut underground = true;
|
||||
for z in -8..6 {
|
||||
canvas.map(Vec3::new(wpos2d.x, wpos2d.y, alt + z), |b| {
|
||||
if b.kind() == BlockKind::Snow {
|
||||
/*if b.kind() == BlockKind::Snow {
|
||||
underground = false;
|
||||
b.into_vacant()
|
||||
} else if b.is_filled() {
|
||||
} else */if b.is_filled() {
|
||||
if b.is_terrain() {
|
||||
Block::new(
|
||||
BlockKind::Earth,
|
||||
@ -1196,10 +1196,10 @@ impl Site {
|
||||
} else {
|
||||
SpriteKind::Empty
|
||||
};
|
||||
if b.kind() == BlockKind::Snow {
|
||||
/* if b.kind() == BlockKind::Snow {
|
||||
underground = false;
|
||||
b.into_vacant().with_sprite(sprite)
|
||||
} else if b.is_filled() {
|
||||
} else */if b.is_filled() {
|
||||
if b.is_terrain() {
|
||||
Block::new(
|
||||
BlockKind::Earth,
|
||||
@ -1227,7 +1227,7 @@ impl Site {
|
||||
BlockKind::Grass,
|
||||
BlockKind::Earth,
|
||||
BlockKind::Sand,
|
||||
BlockKind::Snow,
|
||||
/* BlockKind::Snow, */
|
||||
BlockKind::Rock,
|
||||
]
|
||||
.contains(&b.kind()) {
|
||||
|
Loading…
Reference in New Issue
Block a user