/// <summary> /// Runs a fixed number of lighting propagation updates, /// and returns a set of the chunk ids alterred by these updates. /// </summary> /// <returns></returns> public HashSet <Vector3Int> Update() { Profiler.BeginSample("LightManagerUpdate"); //Process a finite number of the pending updates. int processedThisUpdate = 0; Queue <WipPropagationUpdate> jobsInProgress = new Queue <WipPropagationUpdate>(); var touchedByUpdate = new HashSet <Vector3Int>(chunksWhereLightChangedOnBorderSinceLastUpdate); chunksWhereLightChangedOnBorderSinceLastUpdate.Clear(); while (processedThisUpdate < MaxLightUpdates && pendingLightUpdatesOrder.Count > 0) { var chunkId = pendingLightUpdatesOrder.Dequeue(); var chunkUpdate = pendingLightUpdatesByChunk[chunkId]; pendingLightUpdatesByChunk.Remove(chunkId); if (!chunkManager.IsChunkFullyGenerated(chunkId)) { //skip, as this chunk is no longer valid for updates continue; } touchedByUpdate.Add(chunkId); var data = getJobData(chunkId); Assert.IsTrue(chunkUpdate.borderUpdateRequests.Count > 0); //Run border resolution jobs for each face update request //common queues var dynamicPropagationQueue = new NativeQueue <int3>(Allocator.Persistent); var sunlightPropagationQueue = new NativeQueue <int3>(Allocator.Persistent); BorderResolutionJob[] borderResolutionJobs = new BorderResolutionJob[chunkUpdate.borderUpdateRequests.Count]; for (int i = 0; i < chunkUpdate.borderUpdateRequests.Count; i++) { var borderUpdate = chunkUpdate.borderUpdateRequests[i]; BorderResolutionJob borderJob = new BorderResolutionJob() { data = data, dynamicPropagationQueue = dynamicPropagationQueue, sunlightPropagationQueue = sunlightPropagationQueue, dynamicFromBorder = borderUpdate.dynamic.ToNative(Allocator.Persistent), sunlightFromBorder = borderUpdate.sunlight.ToNative(Allocator.Persistent), toDirection = borderUpdate.borderDirection }; borderResolutionJobs[i] = borderJob; } ///Just one propagation job will be run, with propagation queues ///derived from all the border update requests LightPropagationJob propJob = new LightPropagationJob() { data = data, sunlightNeighbourUpdates = new LightJobNeighbourUpdates(Allocator.Persistent), dynamicNeighbourUpdates = new LightJobNeighbourUpdates(Allocator.Persistent), sunlightPropagationQueue = sunlightPropagationQueue, dynamicPropagationQueue = dynamicPropagationQueue, lightsChangedOnBorder = new NativeArray <bool>(DirectionExtensions.numDirections, Allocator.Persistent) }; if (Parallel) { WipPropagationUpdate updateJob = new WipPropagationUpdate() { borderJobs = borderResolutionJobs, propJob = propJob }; JobHandle handle = new JobHandle(); //Chain dependencies of border resolution jobs for (int i = 0; i < borderResolutionJobs.Length; i++) { handle = borderResolutionJobs[i].Schedule(handle); } handle = propJob.Schedule(handle); updateJob.handle = handle; jobsInProgress.Enqueue(updateJob); } else { for (int i = 0; i < borderResolutionJobs.Length; i++) { borderResolutionJobs[i].Run(); borderResolutionJobs[i].Dispose(); } propJob.Run(); var chunkdata = chunkManager.GetChunkData(data.chunkId.ToBasic()); chunkdata.SetLightMap(data.lights.ToArray()); ///If any light value changes on any of the borders (not just the ones from which ///propagation started), the corresponding neighbour will need to be re-meshed. for (int i = 0; i < propJob.lightsChangedOnBorder.Length; i++) { var neighbourChunkId = chunkId + DirectionExtensions.Vectors[i]; if (propJob.lightsChangedOnBorder[i]) { touchedByUpdate.Add(neighbourChunkId); } } QueuePropagationUpdates(propJob); propJob.Dispose(); } processedThisUpdate++; } Assert.AreEqual(pendingLightUpdatesOrder.Count, pendingLightUpdatesByChunk.Count); //Complete the jobs while (jobsInProgress.Count > 0) { var wip = jobsInProgress.Dequeue(); wip.handle.Complete(); for (int i = 0; i < wip.borderJobs.Length; i++) { wip.borderJobs[i].Dispose(); } var chunkId = wip.propJob.data.chunkId.ToBasic(); var chunkdata = chunkManager.GetChunkData(chunkId); chunkdata.SetLightMap(wip.propJob.data.lights.ToArray()); ///If any light value changes on any of the borders (not just the ones from which ///propagation started), the corresponding neighbour will need to be re-meshed. for (int i = 0; i < wip.propJob.lightsChangedOnBorder.Length; i++) { var neighbourChunkId = chunkId + DirectionExtensions.Vectors[i]; if (wip.propJob.lightsChangedOnBorder[i]) { touchedByUpdate.Add(neighbourChunkId); } } QueuePropagationUpdates(wip.propJob); wip.propJob.Dispose(); } Profiler.EndSample(); return(touchedByUpdate); }
public AbstractPipelineJob <LightmapGenerationJobResult> CreateGenerationJob(Vector3Int chunkId) { int[] heightMap = heightMapProvider.GetHeightMapForColumn(new Vector2Int(chunkId.x, chunkId.z)); var jobData = getJobData(chunkId); var generationJob = new LightGenerationJob() { data = jobData, dynamicPropagationQueue = new NativeQueue <int3>(Allocator.Persistent), sunlightPropagationQueue = new NativeQueue <int3>(Allocator.Persistent), heightmap = heightMap.ToNative() }; var propagationJob = new LightPropagationJob() { data = jobData, sunlightPropagationQueue = generationJob.sunlightPropagationQueue, dynamicPropagationQueue = generationJob.dynamicPropagationQueue, sunlightNeighbourUpdates = new LightJobNeighbourUpdates(Allocator.Persistent), dynamicNeighbourUpdates = new LightJobNeighbourUpdates(Allocator.Persistent), lightsChangedOnBorder = new NativeArray <bool>(DirectionExtensions.numDirections, Allocator.Persistent) }; Func <LightmapGenerationJobResult> cleanup = () => { var result = new LightmapGenerationJobResult(); result.lights = jobData.lights.ToArray(); generationJob.Dispose(); QueuePropagationUpdates(propagationJob); ///If any light value changes on any of the borders ///the corresponding neighbour will need to be re-meshed. for (int i = 0; i < propagationJob.lightsChangedOnBorder.Length; i++) { var neighbourChunkId = chunkId + DirectionExtensions.Vectors[i]; if (propagationJob.lightsChangedOnBorder[i]) { chunksWhereLightChangedOnBorderSinceLastUpdate.Add(neighbourChunkId); } } propagationJob.Dispose(); return(result); }; if (!Parallel) { return(new BasicFunctionJob <LightmapGenerationJobResult>(() => { generationJob.Run(); propagationJob.Run(); return cleanup(); })); } var genHandle = generationJob.Schedule(); var propHandle = propagationJob.Schedule(genHandle); return(new PipelineUnityJob <LightmapGenerationJobResult>(propHandle, cleanup)); }