diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 13121fe75f..dd299fa892 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -286,6 +286,7 @@ pub struct Terrain { mesh_recv: channel::Receiver, mesh_todo: HashMap, ChunkMeshState>, mesh_todos_active: Arc, + mesh_recv_overflow: f32, // GPU data sprite_data: Arc>>, @@ -525,6 +526,7 @@ impl Terrain { mesh_recv: recv, mesh_todo: HashMap::default(), mesh_todos_active: Arc::new(AtomicU64::new(0)), + mesh_recv_overflow: 0.0, sprite_data: sprite_render_context.sprite_data, sprite_col_lights: sprite_render_context.sprite_col_lights, waves: renderer @@ -909,11 +911,18 @@ impl Terrain { drop(guard); // Receive a chunk mesh from a worker thread and upload it to the GPU, then - // store it. Only pull out one chunk per frame to avoid an unacceptable - // amount of blocking lag due to the GPU upload. That still gives us a - // 60 chunks / second budget to play with. + // store it. Vary the rate at which we pull items out to correlate with the + // framerate, preventing tail latency. span!(guard, "Get/upload meshed chunk"); - if let Ok(response) = self.mesh_recv.recv_timeout(Duration::new(0, 0)) { + const CHUNKS_PER_SECOND: f32 = 240.0; + let recv_count = + scene_data.state.get_delta_time() * CHUNKS_PER_SECOND + self.mesh_recv_overflow; + self.mesh_recv_overflow = recv_count.fract(); + let incoming_chunks = + std::iter::from_fn(|| self.mesh_recv.recv_timeout(Duration::new(0, 0)).ok()) + .take(recv_count.floor() as usize) + .collect::>(); // Avoid ownership issue + for response in incoming_chunks { match self.mesh_todo.get(&response.pos) { // It's the mesh we want, insert the newly finished model into the terrain model // data structure (convert the mesh to a model first of course).