Created
October 3, 2017 10:00
-
-
Save Flavelius/e36980bbbbe735a4eb18c1ff80f775b2 to your computer and use it in GitHub Desktop.
Simple Unity3D/Generic Main thread Job Scheduler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
public class JobScheduler | |
{ | |
public delegate float TimeRetrieverFunc(); | |
readonly int MaxPooledJobs = 10000; | |
List<Job> jobs = new List<Job>(1024); | |
Queue<Job> jobPool = new Queue<Job>(); | |
TimeRetrieverFunc getTime; | |
#if !UNITY_STANDALONE && !UNITY_EDITOR | |
/// <summary> | |
/// Creates a new JobScheduler | |
/// </summary> | |
/// <param name="timeFunc">Func which returns the current time (must not be null)</param> | |
/// <param name="maxPoolSize">Size of the pool for inactive task jobs</param> | |
/// <param name="preFillCount">Number of pregenerated jobs to ease allocations at runtime</param> | |
public JobScheduler(TimeRetrieverFunc timeFunc, int maxPoolSize = 10000, int preFillCount = 1000) | |
{ | |
if (timeFunc == null) throw new ArgumentNullException("timeFunc"); | |
getTime = timeFunc; | |
MaxPooledJobs = maxPoolSize; | |
PreFillPool(preFillCount); | |
} | |
#else | |
/// <summary> | |
/// Creates a new JobScheduler | |
/// </summary> | |
/// <param name="maxPoolSize">Size of the pool for inactive task jobs</param> | |
/// <param name="preFillCount">Number of pregenerated jobs to ease allocations at runtime</param> | |
public JobScheduler(int maxPoolSize = 10000, int preFillCount = 1000) | |
{ | |
getTime = () => UnityEngine.Time.time; | |
MaxPooledJobs = maxPoolSize; | |
PreFillPool(preFillCount); | |
} | |
#endif | |
void PreFillPool(int count) | |
{ | |
for (var i = 0; i < count; i++) | |
{ | |
jobPool.Enqueue(new Job(-1, null)); | |
} | |
} | |
public int GetPoolCount() | |
{ | |
return jobPool.Count; | |
} | |
public int GetJobCount() | |
{ | |
return jobs.Count; | |
} | |
void Pool(Job job) | |
{ | |
if (jobPool.Count > MaxPooledJobs) return; | |
job.Reset(); | |
jobPool.Enqueue(job); | |
} | |
Job GetPooledJob(Action task, float scheduledTime) | |
{ | |
if (jobPool.Count > 0) | |
{ | |
var job = jobPool.Dequeue(); | |
job.Setup(scheduledTime, task); | |
return job; | |
} | |
return new Job(scheduledTime, task); | |
} | |
/// <summary> | |
/// Schedules a task for later execution, consider caching the passed Action if possible (less allocations) | |
/// </summary> | |
/// <param name="task">The task to execute</param> | |
/// <param name="delay">Timeframe to wait before executing the task</param> | |
public void Add(Action task, float delay) | |
{ | |
var job = GetPooledJob(task, getTime() + delay); | |
var index = jobs.BinarySearch(job); | |
if (index < 0) index = ~index; | |
jobs.Insert(index, job); | |
} | |
/// <summary> | |
/// Updates the Scheduler so tasks are executed on the main thread | |
/// </summary> | |
public void Update() | |
{ | |
Job job; | |
var time = getTime(); | |
while (jobs.Count > 0 && (job = jobs[jobs.Count-1]).ScheduledTime < time) | |
{ | |
job.Task(); | |
jobs.RemoveAt(jobs.Count-1); | |
Pool(job); | |
} | |
} | |
class Job : IComparable<Job> | |
{ | |
public float ScheduledTime; | |
public Action Task; | |
public Job(float scheduledTime, Action task) | |
{ | |
ScheduledTime = scheduledTime; | |
Task = task; | |
} | |
public int CompareTo(Job other) | |
{ | |
if (ScheduledTime > other.ScheduledTime) return -1; | |
if (ScheduledTime < other.ScheduledTime) return 1; | |
return 0; | |
} | |
public void Setup(float scheduledTime, Action task) | |
{ | |
ScheduledTime = scheduledTime; | |
Task = task; | |
} | |
public void Reset() | |
{ | |
Task = null; | |
ScheduledTime = -1; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment