So I am trying to multithread a Voxel engine in C# made with Sharpdx and I am having a few issues:
This is the class I have :
#region
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using HyroVoxelEngine.Voxels.Chunks;
using HyroVoxelEngine.Voxels.Comparators;
using HyroVoxelEngine.Voxels.Meshing;
using HyroVoxelEngine.World.Cameras;
using HyroVoxelEngine.World.Procedural.Terrain;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Toolkit;
#endregion
namespace HyroVoxelEngine.Voxels
{
public class ChunkManager
{
private GreedyMeshing Mesher = new GreedyMeshing();
private static ConcurrentDictionary<ChunkIndex, Chunk> Cache;
private static ConcurrentQueue<Thread> BuildingThreads = new ConcurrentQueue<Thread>();
private readonly List<Chunk> LoadList;
private readonly int MaxChunkLoadPerFrame = 1;
private readonly List<Chunk> RebuildList;
private readonly List<Chunk> VisibilityList;
public FPSCamera Camera;
private bool ForceVisibilityUpdate = true;
public List<Chunk> RenderList;
private ChunkIndex lastIndex;
private Matrix lastView;
private volatile int taskCount = 10;
private volatile int buildCount = 1;
#region Loading and Building
private void Load(Chunk c)
{
if (c.State != Chunk.ChunkState.New)
return;
new Thread(() =>
{
c.State = Chunk.ChunkState.Generating;
taskCount--;
PlanetaryGenerator.GeneratePlain(c);
taskCount++;
c.State = Chunk.ChunkState.Complete;
}).Start();
}
private void Rebuild(Chunk c)
{
if (c.State != Chunk.ChunkState.NeedBuilding)
return;
if (buildCount > 0)
Task.Run(() =>
{
c.State = Chunk.ChunkState.Generating;
Mesher.Mesh(c);
c.State = Chunk.ChunkState.Complete;
});
}
#endregion
public ChunkManager()
{
LoadList = new List<Chunk>();
RenderList = new List<Chunk>();
VisibilityList = new List<Chunk>();
RebuildList = new List<Chunk>();
Cache = new ConcurrentDictionary<ChunkIndex, Chunk>(2, 500, new ChunkIndexComparator());
}
public void Update(GameTime gt)
{
UpdateLoadList();
UpdateRebuildList();
UpdateVisibilityList();
UpdateRenderList();
if (lastView == Camera.ViewMatrix)
return;
lastView = Camera.ViewMatrix;
UpdateRenderList();
}
private void UpdateVisibilityList()
{
if (!ForceVisibilityUpdate && Camera.ChunkIndex == lastIndex && VisibilityList.Count != 0)
for (int i = 0; i < VisibilityList.Count; i++)
{
LoadList.Add(VisibilityList[i]);
}
else
{
Chunk c;
lastIndex = Camera.ChunkIndex;
LoadList.Clear();
VisibilityList.Clear();
int VisibilityRange = Configs.Configs.ViewDistance;
for (var x = -VisibilityRange; x < VisibilityRange; x++)
{
for (var z = -VisibilityRange; z < VisibilityRange; z++)
{
c = GetChunk(Camera.ChunkIndex.X + x , 0, Camera.ChunkIndex.Z +z );
VisibilityList.Add(c);
switch (c.State)
{
case Chunk.ChunkState.New:
LoadList.Add(c);
break;
case Chunk.ChunkState.NeedBuilding:
RebuildList.Add(c);
break;
case Chunk.ChunkState.Complete:
break;
}
}
}
}
VisibilityList.Sort((x, y) => x.DistanceTo(Camera.Position).CompareTo(y.DistanceTo(Camera.Position)));
RebuildList.Sort((x, y) => x.DistanceTo(Camera.Position).CompareTo(y.DistanceTo(Camera.Position)));
LoadList.Sort((x, y) => x.DistanceTo(Camera.Position).CompareTo(y.DistanceTo(Camera.Position)));
ForceVisibilityUpdate = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Chunk GetChunk(long x, int y, long z)
{
var ind = new ChunkIndex(x, z, y);
Chunk c;
if (Cache.TryGetValue(ind, out c))
return c;
c = new Chunk(ind);
if (Cache.TryAdd(ind, c))
{
return c;
}
return null;
}
private void UpdateRenderList()
{
RenderList.Clear();
Chunk c;
for (int i = 0; i < VisibilityList.Count; i++)
{
c = VisibilityList[i];
if (c.Empty)
return;
BoundingBox b = new BoundingBox();
BoundingFrustum f = new BoundingFrustum(Camera.ViewMatrix*Camera.Projection);
if (!f.Intersects(ref c.BoundingBox))
continue;
if ( c.State == Chunk.ChunkState.Complete)
RenderList.Add(c);
}
}
private void UpdateRebuildList()
{
int loadedChunks = 0;
Chunk c;
for (int i = 0; i < RebuildList.Count; i++)
{
//if (i > MaxChunkLoadPerFrame)
// break;
c = RebuildList[i];
if (c.State == Chunk.ChunkState.NeedBuilding)
{
Rebuild(c);
if (c.State == Chunk.ChunkState.Complete)
{
//Rebuild the neighbours
//TODO:: REBUILD THE NEIGHBOURS
loadedChunks++;
ForceVisibilityUpdate = true;
}
}
}
RebuildList.Clear();
}
private void UpdateLoadList()
{
int loadedChunks = 0;
Chunk c;
for (int i = 0; i < LoadList.Count; i++)
{
//if (i > MaxChunkLoadPerFrame)
// break;
c = LoadList[i];
if (c.State != Chunk.ChunkState.Complete)
{
Load(c);
if (c.State == Chunk.ChunkState.NeedBuilding)
{
loadedChunks++;
ForceVisibilityUpdate = true;
}
}
}
LoadList.Clear();
}
}
}
The issue being that some of the chunks get "broken"
Just like that image
My Second issue is that whenever I move the camera, I get "hiccups" and my framerate drops to under 10.
I have profileed my code and obviously my issue with performance is the Meshing, but my CPU is not even maxed out. and since I am running in anothe rthread it should drop my FPS that much. So how and what could I possibly do to fix this issues?
EDIT
private void Load(Chunk c)
{
if (c.State != Chunk.ChunkState.New)
return;
Stopwatch t = new Stopwatch();
t.Start();
if (taskCount > 0)
{
new Thread(() =>
{
GreedyMeshing Mesher = new GreedyMeshing();
c.State = Chunk.ChunkState.Generating;
taskCount--;
PlanetaryGenerator.GeneratePlain(c);
Mesher.Mesh(c);
taskCount++;
c.State = Chunk.ChunkState.Complete;
}).Start();
}
t.Stop();
}
So I have found that this code is actually the one behind the stutters, probably I should Implement a Pool Mechanism for the threads, but still it should be that lenghty to create a thread I think. Any ideas on how to fix this?