From ef973faadf12c1b7b18ccb2c9e6ba768a1ce7d5f Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Thu, 4 Jul 2019 15:38:55 +0100
Subject: [PATCH] Throttled mesh scheduling to improve remesh performance

---
 voxygen/src/scene/terrain.rs | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs
index ed6544eb8c..740a649dd8 100644
--- a/voxygen/src/scene/terrain.rs
+++ b/voxygen/src/scene/terrain.rs
@@ -23,7 +23,7 @@ struct TerrainChunk {
 struct ChunkMeshState {
     pos: Vec2<i32>,
     started_tick: u64,
-    active_worker: bool,
+    active_worker: Option<u64>,
 }
 
 /// A type produced by mesh worker threads corresponding to the position and mesh of a chunk.
@@ -129,7 +129,7 @@ impl Terrain {
                                 ChunkMeshState {
                                     pos,
                                     started_tick: current_tick,
-                                    active_worker: false,
+                                    active_worker: None,
                                 },
                             );
                         }
@@ -147,8 +147,16 @@ impl Terrain {
             .mesh_todo
             .values_mut()
             // Only spawn workers for meshing jobs without an active worker already.
-            .filter(|todo| !todo.active_worker)
+            .filter(|todo| {
+                todo.active_worker
+                    .map(|worker_tick| worker_tick < todo.started_tick)
+                    .unwrap_or(true)
+            })
         {
+            if client.thread_pool().queued_count() > 0 {
+                break;
+            }
+
             // Find the area of the terrain we want. Because meshing needs to compute things like
             // ambient occlusion and edge elision, we also need the borders of the chunk's
             // neighbours too (hence the `- 1` and `+ 1`).
@@ -189,16 +197,17 @@ impl Terrain {
             let pos = todo.pos;
 
             // Queue the worker thread.
+            let started_tick = todo.started_tick;
             client.thread_pool().execute(move || {
                 let _ = send.send(mesh_worker(
                     pos,
                     (min_z as f32, max_z as f32),
-                    current_tick,
+                    started_tick,
                     volume,
                     aabb,
                 ));
             });
-            todo.active_worker = true;
+            todo.active_worker = Some(todo.started_tick);
         }
 
         // Receive a chunk mesh from a worker thread and upload it to the GPU, then store it.
@@ -208,7 +217,7 @@ impl Terrain {
             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).
-                Some(todo) if response.started_tick == todo.started_tick => {
+                Some(todo) if response.started_tick <= todo.started_tick => {
                     self.chunks.insert(
                         response.pos,
                         TerrainChunk {
@@ -230,7 +239,9 @@ impl Terrain {
                         },
                     );
 
-                    self.mesh_todo.remove(&response.pos);
+                    if response.started_tick == todo.started_tick {
+                        self.mesh_todo.remove(&response.pos);
+                    }
                 }
                 // Chunk must have been removed, or it was spawned on an old tick. Drop the mesh
                 // since it's either out of date or no longer needed.