void Start() { switch (Example) { case ExampleType.DummyJobs: var randomSeed = new Random(); Debug.Log("Testing Background Threads"); for (var i = 0; i < 20; i++) { var i1 = i; ThreadManager.Execute((container) => { var random = new Random(randomSeed.Next()); var delay = random.Next(0, 10000); Debug.Log($"Job: {i1} Executes and takes {delay}ms!"); Thread.Sleep(delay); Debug.Log($"Job: {i1} Complete!"); }); } break; case ExampleType.AccessMainThreadVariableBad: ThreadManager.Execute((container) => { Debug.Log("You cannot access certain variables from background threads, only the main thread."); Debug.Log("This will throw an error:"); try { Debug.Log($"Current time: {Time.time}"); } catch (Exception e) { Debug.LogError(e); } }); break; case ExampleType.AccessMainThreadVariableGood: ThreadManager.Execute((container) => { Debug.Log("To access Main Thread only variables, you can use ExecuteOnMainThreadAndWait()"); var time = 0f; ThreadManager.ExecuteOnMainThreadAndWait(() => { time = Time.time; }); Debug.Log($"The time was {time}"); }); break; case ExampleType.PerformActionMainThread: ThreadManager.Execute((container) => { Debug.Log("If you need to do something on the Main Thread (such as Mesh assignment), you can do it with ExecuteOnMainThread()"); Debug.Log("You can check if you are on the Main Thread (but generally you should always know where your code is executing). This action will throw a warning"); MainThreadAction(); Debug.Log("Lets try that again on the Main Thread"); ThreadManager.ExecuteOnMainThread(MainThreadAction); }); break; case ExampleType.PerformAssignmentMainThread: // Lets create a dummy GameObject to work with var go = new GameObject("Dummy", typeof(MeshRenderer), typeof(MeshFilter)); var meshRenderer = go.GetComponent <MeshRenderer>(); var meshFilter = go.GetComponent <MeshFilter>(); ThreadManager.Execute((container) => { Debug.Log("Now lets try making a mesh using the thread pool"); // Generate mesh data var vertices = new List <Vector3>(); var uvs = new List <Vector2>(); var normals = new List <Vector3>(); var indices = new List <int>(); CubeData.AddCube(ref vertices, ref uvs, ref normals, ref indices, Vector3.zero, false); // Convert these lists to arrays for the mesh var verts = vertices.ToArray(); var uv = uvs.ToArray(); var norm = normals.ToArray(); var ind = indices.ToArray(); // Now that we have the data, assign it on the main thread ThreadManager.ExecuteOnMainThread(() => { meshRenderer.material = MeshMaterial; var mesh = new Mesh { vertices = verts, uv = uv, normals = norm, triangles = ind, }; meshFilter.mesh = mesh; Debug.Log("Mesh Assigned!"); }); }); break; case ExampleType.Chunk: var chunk = Instantiate(ChunkScriptPrefab.gameObject, new Vector3(0f, 0f, 0f), Quaternion.identity); break; case ExampleType.ChunkAdvanced: Instantiate(ChunkAdvancedScriptPrefab.gameObject, new Vector3(0f, 0f, 0f), Quaternion.identity); Instantiate(ChunkAdvancedScriptPrefab.gameObject, new Vector3(ChunkAdvancedScript.ChunkWidth, 0f, 0f), Quaternion.identity); Instantiate(ChunkAdvancedScriptPrefab.gameObject, new Vector3(0f, 0f, ChunkAdvancedScript.ChunkDepth), Quaternion.identity); Instantiate(ChunkAdvancedScriptPrefab.gameObject, new Vector3(ChunkAdvancedScript.ChunkWidth, 0f, ChunkAdvancedScript.ChunkDepth), Quaternion.identity); Instantiate(ChunkAdvancedScriptPrefab.gameObject, new Vector3(0f, ChunkAdvancedScript.ChunkHeight, 0f), Quaternion.identity); Instantiate(ChunkAdvancedScriptPrefab.gameObject, new Vector3(ChunkAdvancedScript.ChunkWidth, ChunkAdvancedScript.ChunkHeight, 0f), Quaternion.identity); Instantiate(ChunkAdvancedScriptPrefab.gameObject, new Vector3(0f, ChunkAdvancedScript.ChunkHeight, ChunkAdvancedScript.ChunkDepth), Quaternion.identity); Instantiate(ChunkAdvancedScriptPrefab.gameObject, new Vector3(ChunkAdvancedScript.ChunkWidth, ChunkAdvancedScript.ChunkHeight, ChunkAdvancedScript.ChunkDepth), Quaternion.identity); break; default: throw new ArgumentOutOfRangeException(); } }
/// <summary> /// Thread Safe! Sets a Voxel in this Chunk and starts a Meshing job. /// </summary> private void SetVoxel(int x, int y, int z, byte voxelId) { lock (_debugLocker) { _debugVoxelUpdates++; } // Arrays are reference types, so create a blank array to grab the value to var voxels = new byte[ChunkWidth][][]; for (var x1 = 0; x1 < ChunkWidth; x1++) { voxels[x1] = new byte[ChunkHeight][]; for (var y1 = 0; y1 < ChunkHeight; y1++) { voxels[x1][y1] = new byte[ChunkDepth]; } } // Update the Chunk state and grab the current state lock (_voxelLocker) { if (_voxels[x][y][z] == voxelId) { return; // Don't need to generate a new mesh if the state is unchanged } _voxels[x][y][z] = voxelId; for (var x1 = 0; x1 < ChunkWidth; x1++) { for (var y1 = 0; y1 < ChunkHeight; y1++) { for (var z1 = 0; z1 < ChunkDepth; z1++) { voxels[x1][y1][z1] = _voxels[x1][y1][z1]; } } } } ThreadManager.Execute((container) => { var job = GetNextJobId(); // Generate mesh data var vertices = new List <Vector3>(); var uvs = new List <Vector2>(); var normals = new List <Vector3>(); var indices = new List <int>(); for (var x1 = 0; x1 < ChunkWidth; x1++) { for (var y1 = 0; y1 < ChunkHeight; y1++) { for (var z1 = 0; z1 < ChunkDepth; z1++) { if (voxels[x1][y1][z1] == 0) { continue; } CubeData.AddCube(ref vertices, ref uvs, ref normals, ref indices, new Vector3(x1, y1, z1), false); } } } // Convert these lists to arrays for the mesh var verts = vertices.ToArray(); var uv = uvs.ToArray(); var norm = normals.ToArray(); var ind = indices.ToArray(); ThreadManager.ExecuteOnMainThread(() => { if (GetLastJobId() > job) // Stale Mesh { return; } SetLastJobId(job); SetMesh(verts, uv, norm, ind); }); }); }
void Update() { // Randomly update the Voxel to test its meshing if (Time.time > _updateTime + UpdateFrequency) { _updateTime += UpdateFrequency; for (var i = 0; i < VoxelChangesPerUpdate; i++) { // Attempt to change the Chunk state, searching for a voxel which is different // Normally you would just do // SetVoxel(Random.Range(0, (int)ChunkWidth), Random.Range(0, (int)ChunkHeight), Random.Range(0, (int)ChunkDepth), (byte)Random.Range(0, 2)); // But in this case I wanted to test how fast we can update the Mesh var attempts = 0; var voxel = (byte)Random.Range(0, 2); var x = Random.Range(0, (int)ChunkWidth); var y = Random.Range(0, (int)ChunkHeight); var z = Random.Range(0, (int)ChunkDepth); var outOfAttempts = false; while (GetVoxel(x, y, z) == voxel) { if (attempts > 20) { outOfAttempts = true; break; } voxel = (byte)Random.Range(0, 2); attempts++; } if (outOfAttempts) { continue; } // We found a voxel that differs from the one we are setting, so try setting it SetVoxel(x, y, z, voxel); } } if (_dirty) { _dirty = false; ThreadManager.Execute((container) => { // Get the job Id for this job var job = GetNextJobId(); // Arrays are reference types, so create a blank array to grab the values var voxels = new byte[ChunkWidth][][]; for (var x = 0; x < ChunkWidth; x++) { voxels[x] = new byte[ChunkHeight][]; for (var y = 0; y < ChunkHeight; y++) { voxels[x][y] = new byte[ChunkDepth]; } } // Grab the current state lock (_voxelLocker) { for (var x = 0; x < ChunkWidth; x++) { for (var y = 0; y < ChunkHeight; y++) { for (var z = 0; z < ChunkDepth; z++) { voxels[x][y][z] = _voxels[x][y][z]; } } } } // Grab the Lists from our Container or generate them if (!container.ContainerObjects.ContainsKey("VertexList")) { container.ContainerObjects.Add("VertexList", new List <Vector3>()); } if (!container.ContainerObjects.ContainsKey("UVList")) { container.ContainerObjects.Add("UVList", new List <Vector2>()); } if (!container.ContainerObjects.ContainsKey("NormalList")) { container.ContainerObjects.Add("NormalList", new List <Vector3>()); } if (!container.ContainerObjects.ContainsKey("IndexList")) { container.ContainerObjects.Add("IndexList", new List <int>()); } var vertexList = (List <Vector3>)container.ContainerObjects["VertexList"]; var uvList = (List <Vector2>)container.ContainerObjects["UVList"]; var normalList = (List <Vector3>)container.ContainerObjects["NormalList"]; var indexList = (List <int>)container.ContainerObjects["IndexList"]; vertexList.Clear(); uvList.Clear(); normalList.Clear(); indexList.Clear(); // Generate mesh data for (var x = 0; x < ChunkWidth; x++) { for (var y = 0; y < ChunkHeight; y++) { for (var z = 0; z < ChunkDepth; z++) { if (voxels[x][y][z] == 0) { continue; } CubeData.AddCube(ref vertexList, ref uvList, ref normalList, ref indexList, new Vector3(x, y, z), new byte[] { _getVoxel(voxels, x, y, z - 1), _getVoxel(voxels, x - 1, y, z), _getVoxel(voxels, x, y - 1, z), _getVoxel(voxels, x, y, z + 1), _getVoxel(voxels, x + 1, y, z), _getVoxel(voxels, x, y + 1, z), }, false); } } } // Convert these lists to arrays for the mesh var vertices = vertexList.ToArray(); var uvs = uvList.ToArray(); var normals = normalList.ToArray(); var indices = indexList.ToArray(); ThreadManager.ExecuteOnMainThread(() => { if (GetLastJobId() > job) // Stale Mesh { return; } SetLastJobId(job); SetMesh(vertices, uvs, normals, indices); }); }); } //UpdateDebug(); }