chunk_builder: single-threaded on wasm (#458)

Refactor the chunk builder to use multithreading on native, but no threads on
wasm, at least until we have web workers or wasm threads. With this change
and the shader fix, chunks now render on Chrome (albeit with no textures).
Another step towards 🕸️ Web support #446.

* Single-threaded chunk builder

* shaders: chunk_frag: consistently enable outputs, fixes GL_INVALID_OPERATION on WebGL
This commit is contained in:
ice_iix 2021-01-07 17:26:58 -08:00
parent 8208e3b595
commit fa2e5e8bdb
2 changed files with 173 additions and 132 deletions

View File

@ -43,7 +43,7 @@ impl ChunkBuilder {
let id = i;
threads.push((
work_send,
thread::spawn(move || build_func(id, models, work_recv, built_send)),
thread::spawn(move || build_func_threaded(id, models, work_recv, built_send)),
));
free.push((i, vec![], vec![]));
}
@ -69,32 +69,36 @@ impl ChunkBuilder {
}
}
while let Ok((id, mut val)) = self.built_recv.try_recv() {
world.reset_building_flag(val.position);
if NUM_WORKERS > 0 {
while let Ok((id, mut val)) = self.built_recv.try_recv() {
world.reset_building_flag(val.position);
if let Some(sec) = world.get_section_mut(val.position.0, val.position.1, val.position.2)
{
sec.cull_info = val.cull_info;
renderer.update_chunk_solid(
&mut sec.render_buffer,
&val.solid_buffer,
val.solid_count,
);
renderer.update_chunk_trans(
&mut sec.render_buffer,
&val.trans_buffer,
val.trans_count,
);
if let Some(sec) =
world.get_section_mut(val.position.0, val.position.1, val.position.2)
{
sec.cull_info = val.cull_info;
renderer.update_chunk_solid(
&mut sec.render_buffer,
&val.solid_buffer,
val.solid_count,
);
renderer.update_chunk_trans(
&mut sec.render_buffer,
&val.trans_buffer,
val.trans_count,
);
}
val.solid_buffer.clear();
val.trans_buffer.clear();
self.free_builders
.push((id, val.solid_buffer, val.trans_buffer));
}
if self.free_builders.is_empty() {
return;
}
}
val.solid_buffer.clear();
val.trans_buffer.clear();
self.free_builders
.push((id, val.solid_buffer, val.trans_buffer));
}
if self.free_builders.is_empty() {
return;
}
let dirty_sections = world
.get_render_list()
.iter()
@ -102,22 +106,60 @@ impl ChunkBuilder {
.filter(|v| world.is_section_dirty(*v))
.collect::<Vec<_>>();
for (x, y, z) in dirty_sections {
let t_id = self.free_builders.pop().unwrap();
let t_id = if NUM_WORKERS > 0 {
self.free_builders.pop().unwrap()
} else {
(0, vec![], vec![])
};
world.set_building_flag((x, y, z));
let (cx, cy, cz) = (x << 4, y << 4, z << 4);
let mut snapshot = world.capture_snapshot(cx - 2, cy - 2, cz - 2, 20, 20, 20);
snapshot.make_relative(-2, -2, -2);
self.threads[t_id.0]
.0
.send(BuildReq {
snapshot,
position: (x, y, z),
solid_buffer: t_id.1,
trans_buffer: t_id.2,
})
.unwrap();
if self.free_builders.is_empty() {
return;
if NUM_WORKERS > 0 {
self.threads[t_id.0]
.0
.send(BuildReq {
snapshot,
position: (x, y, z),
solid_buffer: t_id.1,
trans_buffer: t_id.2,
})
.unwrap();
if self.free_builders.is_empty() {
return;
}
} else {
let mut val = build_func_1(
self.models.clone(),
BuildReq {
snapshot,
position: (x, y, z),
solid_buffer: t_id.1,
trans_buffer: t_id.2,
},
);
world.reset_building_flag(val.position);
if let Some(sec) =
world.get_section_mut(val.position.0, val.position.1, val.position.2)
{
sec.cull_info = val.cull_info;
renderer.update_chunk_solid(
&mut sec.render_buffer,
&val.solid_buffer,
val.solid_count,
);
renderer.update_chunk_trans(
&mut sec.render_buffer,
&val.trans_buffer,
val.trans_count,
);
}
val.solid_buffer.clear();
val.trans_buffer.clear();
}
}
}
@ -139,130 +181,133 @@ struct BuildReply {
cull_info: CullInfo,
}
fn build_func(
fn build_func_threaded(
id: usize,
models: Arc<RwLock<model::Factory>>,
work_recv: mpsc::Receiver<BuildReq>,
built_send: mpsc::Sender<(usize, BuildReply)>,
) {
loop {
let BuildReq {
snapshot,
position,
mut solid_buffer,
mut trans_buffer,
} = match work_recv.recv() {
let work: BuildReq = match work_recv.recv() {
Ok(val) => val,
Err(_) => return,
};
let mut rng = rand_pcg::Pcg32::from_seed([
((position.0 as u32) & 0xff) as u8,
(((position.0 as u32) >> 8) & 0xff) as u8,
(((position.0 as u32) >> 16) & 0xff) as u8,
((position.0 as u32) >> 24) as u8,
((position.1 as u32) & 0xff) as u8,
(((position.1 as u32) >> 8) & 0xff) as u8,
(((position.1 as u32) >> 16) & 0xff) as u8,
((position.1 as u32) >> 24) as u8,
((position.2 as u32) & 0xff) as u8,
(((position.2 as u32) >> 8) & 0xff) as u8,
(((position.2 as u32) >> 16) & 0xff) as u8,
((position.2 as u32) >> 24) as u8,
(((position.0 as u32 ^ position.2 as u32) | 1) & 0xff) as u8,
((((position.0 as u32 ^ position.2 as u32) | 1) >> 8) & 0xff) as u8,
((((position.0 as u32 ^ position.2 as u32) | 1) >> 16) & 0xff) as u8,
(((position.0 as u32 ^ position.2 as u32) | 1) >> 24) as u8,
]);
let reply = build_func_1(models.clone(), work);
let mut solid_count = 0;
let mut trans_count = 0;
built_send.send((id, reply)).unwrap();
}
}
for y in 0..16 {
for x in 0..16 {
for z in 0..16 {
let block = snapshot.get_block(x, y, z);
let mat = block.get_material();
if !mat.renderable {
// Use one step of the rng so that
// if a block is placed in an empty
// location is variant doesn't change
let _: u32 = rng.gen();
continue;
}
fn build_func_1(models: Arc<RwLock<model::Factory>>, work: BuildReq) -> BuildReply {
let BuildReq {
snapshot,
position,
mut solid_buffer,
mut trans_buffer,
} = work;
match block {
block::Block::Water { .. } | block::Block::FlowingWater { .. } => {
let tex = models.read().unwrap().textures.clone();
trans_count += model::liquid::render_liquid(
tex,
false,
&snapshot,
x,
y,
z,
&mut trans_buffer,
);
continue;
}
block::Block::Lava { .. } | block::Block::FlowingLava { .. } => {
let tex = models.read().unwrap().textures.clone();
solid_count += model::liquid::render_liquid(
tex,
true,
&snapshot,
x,
y,
z,
&mut solid_buffer,
);
continue;
}
_ => {}
}
let mut rng = rand_pcg::Pcg32::from_seed([
((position.0 as u32) & 0xff) as u8,
(((position.0 as u32) >> 8) & 0xff) as u8,
(((position.0 as u32) >> 16) & 0xff) as u8,
((position.0 as u32) >> 24) as u8,
((position.1 as u32) & 0xff) as u8,
(((position.1 as u32) >> 8) & 0xff) as u8,
(((position.1 as u32) >> 16) & 0xff) as u8,
((position.1 as u32) >> 24) as u8,
((position.2 as u32) & 0xff) as u8,
(((position.2 as u32) >> 8) & 0xff) as u8,
(((position.2 as u32) >> 16) & 0xff) as u8,
((position.2 as u32) >> 24) as u8,
(((position.0 as u32 ^ position.2 as u32) | 1) & 0xff) as u8,
((((position.0 as u32 ^ position.2 as u32) | 1) >> 8) & 0xff) as u8,
((((position.0 as u32 ^ position.2 as u32) | 1) >> 16) & 0xff) as u8,
(((position.0 as u32 ^ position.2 as u32) | 1) >> 24) as u8,
]);
if mat.transparent {
trans_count += model::Factory::get_state_model(
&models,
block,
&mut rng,
let mut solid_count = 0;
let mut trans_count = 0;
for y in 0..16 {
for x in 0..16 {
for z in 0..16 {
let block = snapshot.get_block(x, y, z);
let mat = block.get_material();
if !mat.renderable {
// Use one step of the rng so that
// if a block is placed in an empty
// location is variant doesn't change
let _: u32 = rng.gen();
continue;
}
match block {
block::Block::Water { .. } | block::Block::FlowingWater { .. } => {
let tex = models.read().unwrap().textures.clone();
trans_count += model::liquid::render_liquid(
tex,
false,
&snapshot,
x,
y,
z,
&mut trans_buffer,
);
} else {
solid_count += model::Factory::get_state_model(
&models,
block,
&mut rng,
continue;
}
block::Block::Lava { .. } | block::Block::FlowingLava { .. } => {
let tex = models.read().unwrap().textures.clone();
solid_count += model::liquid::render_liquid(
tex,
true,
&snapshot,
x,
y,
z,
&mut solid_buffer,
);
continue;
}
_ => {}
}
if mat.transparent {
trans_count += model::Factory::get_state_model(
&models,
block,
&mut rng,
&snapshot,
x,
y,
z,
&mut trans_buffer,
);
} else {
solid_count += model::Factory::get_state_model(
&models,
block,
&mut rng,
&snapshot,
x,
y,
z,
&mut solid_buffer,
);
}
}
}
}
let cull_info = build_cull_info(&snapshot);
let cull_info = build_cull_info(&snapshot);
built_send
.send((
id,
BuildReply {
position,
solid_buffer,
solid_count,
trans_buffer,
trans_count,
cull_info,
},
))
.unwrap();
BuildReply {
position,
solid_buffer,
solid_count,
trans_buffer,
trans_count,
cull_info,
}
}

View File

@ -6,13 +6,11 @@ in vec2 vTextureOffset;
in float vAtlas;
in vec3 vLighting;
#ifndef alpha
#ifdef ES
layout(location = 2) out vec4 fragColor;
#else
out vec4 fragColor;
#endif
#else
#ifdef ES
layout(location = 0) out vec4 accum;
@ -22,8 +20,6 @@ out vec4 accum;
out float revealage;
#endif
#endif
#include lookup_texture
void main() {