So I am developing a C# Application, which is quite CPU intensive.
Currently I am using ThreadPool to process the tasks async, but this is proving to be not working as I expected.
Take this class I use this to retrive a Builder class to generate a chunk.
public class ChunkBuilderProvider
{
private readonly BlockingCollection<ChunkBuilder> Builders;
public ChunkBuilderProvider()
{
Builders = new BlockingCollection<ChunkBuilder>();
for (int i = 0; i < Configs.BuilderMaxInstance; i++)
Builders.Add(new ChunkBuilder());
}
public ChunkBuilder GetBuilder()
{
ChunkBuilder c;
return Builders.TryTake(out c, -1) ? c : null;
}
public void ReplaceBuilder(ChunkBuilder c)
{
Builders.Add(c);
}
public int IdleBuilders()
{
return Builders.Count;
}
internal bool Build(Chunk c)
{
if (c.State == Chunk.ChunkState.Generating)
return false;
var b = GetBuilder();
if (b == null)
return false;
ThreadPool.QueueUserWorkItem(a =>
{
b.Generate(c);
ReplaceBuilder(b);
});
return true;
}
}
The Generate task is VERY CPU intensive, running this with 5 builders, bursts my CPU to 100% usage.
Ants Shows me this :
[Seems like I can't post images here]
Edit: The CPU intensive code is this :
using System;
using System.Collections.Generic;
using HyroVoxelEngine.Graphics.Primitives;
using HyroVoxelEngine.Voxels.Blocks;
using HyroVoxelEngine.Voxels.Chunks;
using SharpDX;
namespace HyroVoxelEngine.Voxels.Meshing
{
public class GreedyMeshing
{
private static readonly int[][][] VerticesOffset = new int[6][][]
{
//TOP
new int[9][]
{
new int[3] {-1, 1, 1}, new int[3] {0, 1, 1}, new int[3] {1, 1, 1}, new int[3] {-1, 1, 0}, new int[3] {0, 1, 0}, new int[3] {1, 1, 0}, new int[3] {-1, 1, -1},
new int[3] {0, 1, -1}, new int[3] {1, 1, -1}
},
//North
new int[9][]
{
new int[3] {-1, -1, 1}, new int[3] {0, -1, 1}, new int[3] {1, -1, 1}, new int[3] {-1, 0, 1}, new int[3] {0, 0, 1}, new int[3] {1, 0, 1}, new int[3] {-1, 1, 1},
new int[3] {0, 1, 1}, new int[3] {1, 1, 1}
},
//Bottom
new int[9][]
{
new int[3] {-1, -1, -1}, new int[3] {0, -1, -1}, new int[3] {1, -1, -1}, new int[3] {-1, -1, 0}, new int[3] {0, -1, 0}, new int[3] {1, -1, 0}, new int[3] {-1, -1, 1},
new int[3] {0, -1, 1}, new int[3] {1, -1, 1}
},
//SOUTH
new int[9][]
{
new int[3] {-1, 1, -1}, new int[3] {0, 1, -1}, new int[3] {1, 1, -1}, new int[3] {-1, 0, -1}, new int[3] {0, 0, -1}, new int[3] {1, 0, -1}, new int[3] {-1, -1, -1},
new int[3] {0, -1, -1}, new int[3] {1, -1, -1}
},
//West
new int[9][]
{
new int[3] {1, 1, 1}, new int[3] {1, 0, 1}, new int[3] {1, -1, 1}, new int[3] {1, 1, 0}, new int[3] {1, 0, 0}, new int[3] {1, -1, 0}, new int[3] {1, 1, -1},
new int[3] {1, 0, -1}, new int[3] {1, -1, -1}
},
//East
new int[9][]
{
new int[3] {-1, -1, 1}, new int[3] {-1, 0, 1}, new int[3] {-1, 1, 1}, new int[3] {-1, -1, 0}, new int[3] {-1, 0, 0}, new int[3] {-1, 1, 0}, new int[3] {-1, -1, -1},
new int[3] {-1, 0, -1}, new int[3] {-1, 1, -1}
}
};
private Block[][][] Blocks;
private List<int> Index;
private int VOXEL_SIZE = 1;
private Chunk chunk;
private List<VoxelVertex> vertices;
public void SetChunk(Chunk c)
{
chunk = c;
Blocks = c.Blocks;
}
public ChunkPrimitive Greedy()
{
Index = new List<int>(10000);
vertices = new List<VoxelVertex>(8000);
/*
* These are just working variables for the algorithm - almost all taken
* directly from Mikola Lysenko's javascript implementation.
*/
int i, j, k, l, w, h, u, v, n;
var side = VoxelFace.Direction.None;
int[] x = {0, 0, 0};
int[] q = {0, 0, 0};
int[] du = {0, 0, 0};
int[] dv = {0, 0, 0};
/*
* We create a mask - this will contain the groups of matching voxel faces
* as we proceed through the chunk in 6 directions - once for each face.
*/
VoxelFace voxelFace, voxelFace1;
int[] Dimensions = {Chunk.SizeX, Chunk.SizeY, Chunk.SizeZ};
VoxelFace[] mask;
/**
* We start with the lesser-spotted boolean for-loop (also known as the old flippy floppy).
*
* The variable backFace will be TRUE on the first iteration and FALSE on the second - this allows
* us to track which direction the indices should run during creation of the quad.
*
* This loop runs twice, and the inner loop 3 times - totally 6 iterations - one for each
* voxel face.
*/
for (bool backFace = true, b = false; b != backFace; backFace = backFace && b, b = !b)
{
/*
* We sweep over the 3 dimensions - most of what follows is well described by Mikola Lysenko
* in his post - and is ported from his Javascript implementation. Where this implementation
* diverges, I've added commentary.
*/
for (int d = 0; d < 3; d++)
{
/*
* These are just working variables to hold two faces during comparison.
*/
u = (d + 1)%3;
v = (d + 2)%3;
x[0] = 0;
x[1] = 0;
x[2] = 0;
q[0] = 0;
q[1] = 0;
q[2] = 0;
q[d] = 1;
mask = new VoxelFace[Dimensions[u]*Dimensions[v]];
/*
* Here we're keeping track of the side that we're meshing.
*/
if (d == 0)
side = backFace ? VoxelFace.Direction.West : VoxelFace.Direction.East;
else if (d == 1)
side = backFace ? VoxelFace.Direction.Bottom : VoxelFace.Direction.Top;
else if (d == 2)
side = backFace ? VoxelFace.Direction.South : VoxelFace.Direction.North;
/*
* We move through the dimension from front to back
*/
for (x[d] = -1; x[d] < Dimensions[d];)
{
n = 0;
for (x[v] = 0; x[v] < Dimensions[v]; x[v]++)
{
for (x[u] = 0; x[u] < Dimensions[u]; x[u]++)
{
/*
* Here we retrieve two voxel faces for comparison.
*/
voxelFace = (x[d] >= 0) ? getVoxelFace(x[0], x[1], x[2], side) : null;
voxelFace1 = (x[d] < Dimensions[d] - 1) ? getVoxelFace(x[0] + q[0], x[1] + q[1], x[2] + q[2], side) : null;
mask[n++] = ((voxelFace != null && voxelFace1 != null && voxelFace.Equals(voxelFace1))) ? null : backFace ? voxelFace1 : voxelFace;
}
}
x[d]++;
/*
* Now we generate the mesh for the mask
*/
n = 0;
for (j = 0; j < Dimensions[v]; j++)
{
for (i = 0; i < Dimensions[u];)
{
if (mask[n] != null)
{
/*
* We compute the width
*/
for (w = 1; i + w < Dimensions[u] && mask[n + w] != null && mask[n + w].Equals(mask[n]); w++) {}
/*
* Then we compute height
*/
bool done = false;
for (h = 1; j + h < Dimensions[v]; h++)
{
for (k = 0; k < w; k++)
{
if (mask[n + k + h*Dimensions[u]] == null || !mask[n + k + h*Dimensions[u]].Equals(mask[n]))
{
done = true;
break;
}
}
if (done)
break;
}
/*
* Here we check the "transparent" attribute in the VoxelFace class to ensure that we don't mesh
* any culled faces.
*/
if (!mask[n].Transparent)
{
/*
* Add quad
*/
x[u] = i;
x[v] = j;
du[0] = 0;
du[1] = 0;
du[2] = 0;
du[u] = w;
dv[0] = 0;
dv[1] = 0;
dv[2] = 0;
dv[v] = h;
/*
* And here we call the quad function in order to render a merged quad in the scene.
*
* We pass mask[n] to the function, which is an instance of the VoxelFace class containing
* all the attributes of the face - which allows for variables to be passed to shaders - for
* example lighting values used to create ambient occlusion.
*/
Quad(new Vector3(x[0], x[1], x[2]), new Vector3(x[0] + du[0], x[1] + du[1], x[2] + du[2]),
new Vector3(x[0] + du[0] + dv[0], x[1] + du[1] + dv[1], x[2] + du[2] + dv[2]), new Vector3(x[0] + dv[0], x[1] + dv[1], x[2] + dv[2]), w, h,
mask[n], backFace);
}
/*
* We zero out the mask
*/
for (l = 0; l < h; ++l)
{
for (k = 0; k < w; ++k)
mask[n + k + l*Dimensions[u]] = null;
}
/*
* And then finally increment the counters and continue
*/
i += w;
n += w;
}
else
{
i++;
n++;
}
}
}
}
}
}
if (vertices.Count == 0 || Index.Count == 0)
return null;
return new ChunkPrimitive(vertices.ToArray(), Index.ToArray());
}
private VoxelFace getVoxelFace(int x, int y, int z, VoxelFace.Direction side)
{
VoxelFace voxelFace = new VoxelFace(side);
voxelFace.Type = Blocks[x][y][z].Type;
voxelFace.Light = chunk.LightValue[x][y][z];
voxelFace.Side = side;
voxelFace.LightSettings = CountSolidCorner(voxelFace, x, y, z);
return voxelFace;
}
private int[] CountSolidCorner(VoxelFace voxelFace, int x, int y, int z)
{
var side = voxelFace.Side;
int bottomLeft = 0;
int bottomRight = 0;
int TopLeft = 0;
int TopRight = 0;
var pos = new Vector3(x, y, z);
#region TOP BOTOM
//SOUTH = -z
//NORTH = +z
//West = -X
//est = X;
int[][] vertOff = VerticesOffset[(int) side];
if (GetBlockSolid(vertOff[6], x, y, z))
bottomLeft++;
if (GetBlockSolid(vertOff[8], x, y, z))
bottomRight++;
if (GetBlockSolid(vertOff[2], x, y, z))
TopRight++;
if (GetBlockSolid(vertOff[0], x, y, z))
TopLeft++;
if (GetBlockSolid(vertOff[1], x, y, z))
{
TopLeft++;
TopRight++;
}
if (GetBlockSolid(vertOff[7], x, y, z))
{
bottomLeft++;
bottomRight++;
}
if (GetBlockSolid(vertOff[3], x, y, z))
{
TopLeft++;
bottomLeft++;
}
if (GetBlockSolid(vertOff[5], x, y, z))
{
TopRight++;
bottomRight++;
}
if (side == VoxelFace.Direction.Bottom)
return new[] {TopLeft, TopRight, bottomLeft, bottomRight};
if (side == VoxelFace.Direction.Top)
return new[] {bottomLeft, bottomRight, TopLeft, TopRight};
if (side == VoxelFace.Direction.West)
return new[] {bottomLeft, TopLeft, bottomRight, TopRight};
if (side == VoxelFace.Direction.East)
return new[] {bottomRight, TopRight, bottomLeft, TopLeft};
if (side == VoxelFace.Direction.North)
return new[] {TopLeft, bottomLeft, TopRight, bottomRight};
#endregion
//COM x Positivo
//TOP - TR - BR - TL - BL
return new[] {4, 4, 4, 4};
}
private bool GetBlockSolid(int[] offset, int x, int y, int z)
{
x = x + offset[0];
y = y + offset[1];
z = z + offset[2];
if (x < 0 | y < 0 | z < 0)
return true;
if (x >= Chunk.SizeX | y >= Chunk.SizeY | z >= Chunk.SizeZ)
return true;
return !Block.IsSolidBlock(Blocks[x][y ][z ].Type);
}
private void Quad(Vector3 bottomLeft, Vector3 topLeft, Vector3 topRight, Vector3 bottomRight, int width, int height, VoxelFace voxel, bool backFace)
{
BlockTexture texture = new BlockTexture();
Vector2[] UVList = new Vector2[4];
var vert = new VoxelVertex[4];
Vector3 normal = voxel.Normal;
if (voxel.Side == VoxelFace.Direction.Top) {}
//switch (voxel.Side)
//{
// case VoxelFace.Direction.Bottom:
// normal = - Vector3.UnitY;
// texture = TextureHelper.GetTexture(voxel.Type, BlockFaceDirection.YDecreasing);
// UVList = TextureHelper.GetUVMapping((int) texture, BlockFaceDirection.YDecreasing);
// break;
// case VoxelFace.Direction.Top:
// normal = Vector3.UnitY;
// texture = TextureHelper.GetTexture(voxel.Type, BlockFaceDirection.YIncreasing);
// UVList = TextureHelper.GetUVMapping((int) texture, BlockFaceDirection.YIncreasing);
// break;
// case VoxelFace.Direction.West:
// normal = Vector3.UnitX;
// texture = TextureHelper.GetTexture(voxel.Type, BlockFaceDirection.XIncreasing);
// UVList = TextureHelper.GetUVMapping((int) texture, BlockFaceDirection.XIncreasing);
// break;
// case VoxelFace.Direction.East:
// normal = -Vector3.UnitX;
// texture = TextureHelper.GetTexture(voxel.Type, BlockFaceDirection.XDecreasing);
// UVList = TextureHelper.GetUVMapping((int) texture, BlockFaceDirection.XDecreasing);
// break;
// case VoxelFace.Direction.North:
// normal = -Vector3.UnitZ;
// texture = TextureHelper.GetTexture(voxel.Type, BlockFaceDirection.ZDecreasing);
// UVList = TextureHelper.GetUVMapping((int) texture, BlockFaceDirection.ZDecreasing);
// break;
// case VoxelFace.Direction.South:
// normal = Vector3.UnitZ;
// texture = TextureHelper.GetTexture(voxel.Type, BlockFaceDirection.ZIncreasing);
// UVList = TextureHelper.GetUVMapping((int) texture, BlockFaceDirection.ZIncreasing);
// break;
//}
int ic = (Index.Count/6)*4;
int[] indexes = !backFace ? new[] {2, 0, 1, 1, 3, 2} : new[] {2, 3, 1, 1, 0, 2};
vert[0] = new VoxelVertex(bottomLeft*(VOXEL_SIZE), normal, UVList[0], new Vector3(voxel.Light, voxel.LightSettings[0], (int) (voxel.Side)));
vert[1] = new VoxelVertex(bottomRight*(VOXEL_SIZE), normal, UVList[1], new Vector3(voxel.Light, voxel.LightSettings[1], (int) (voxel.Side)));
vert[2] = new VoxelVertex(topLeft*(VOXEL_SIZE), normal, UVList[2], new Vector3(voxel.Light, voxel.LightSettings[2], (int) (voxel.Side)));
vert[3] = new VoxelVertex(topRight*(VOXEL_SIZE), normal, UVList[3], new Vector3(voxel.Light, voxel.LightSettings[3], (int) (voxel.Side)));
if (voxel.LightSettings[0] + voxel.LightSettings[3] > voxel.LightSettings[1] + voxel.LightSettings[2])
indexes = !backFace ? new[] {0, 1, 3, 3, 2, 0} : new[] {0, 2, 3, 3, 1, 0};
//int[] indexes = !backFace ? new[] { 0, 3, 2, 2, 1, 0 } : new[] { 0, 1, 2, 2, 0, 2 };
Index.Add(indexes[0] + ic);
Index.Add(indexes[1] + ic);
Index.Add(indexes[2] + ic);
Index.Add(indexes[3] + ic);
Index.Add(indexes[4] + ic);
Index.Add(indexes[5] + ic);
vertices.Add(vert[0]);
vertices.Add(vert[1]);
vertices.Add(vert[2]);
vertices.Add(vert[3]);
}
internal void Dispose()
{
throw new NotImplementedException();
}
}
}
It is highly depends on how the ChunkBuilder is thead-safe itself. If it hasn't its own chunk-dependent state (and this is good design), you don't need the builders collection. And all you flow can looks as simple as:
using System.Threading.Tasks;
...
public IEnumerable<Chunk> GetChunks()
{
// Here you can return the whole chunks collection or yield them one by one.
yield return new Chunk();
}
public void DoIt()
{
ChunkBuilder builder = new ChunkBuilder();
ParallelOptions options = new ParallelOptions() {
MaxDegreeOfParallelism = 4
};
Parallel.ForEach(this.GetChunks(), options, chunk => builder.Generate(chunk));
}
If you ChunkBuilder contains chunk processing state, then there are two choices:
1) If ChunkBuilder initialization is simple and fast, just create builder when processing required:
Parallel.ForEach(this.GetChunks(), options, chunk => new ChunkBuilder().Generate(chunk));
2) If ChunkBuilder is slow to construct, its better to redesign according to SRP rules a little and extract chunk processing state into separate object.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.