Skip to content

Instantly share code, notes, and snippets.

@simonwittber
Created August 1, 2021 13:55
Show Gist options
  • Save simonwittber/655745b311e0ac67f1c65f20ac5d65e4 to your computer and use it in GitHub Desktop.
Save simonwittber/655745b311e0ac67f1c65f20ac5d65e4 to your computer and use it in GitHub Desktop.
Component Systems using Jobs and Burst
using System.Collections.Generic;
using Unity.Jobs;
using UnityEngine;
static internal class ComponentBatch<T, U, V>
where T : MonoBehaviourComponent<T, U, V> where U : MonoBehaviourSystem<T, U, V>
where V: struct, IJobParallelFor
{
internal static Queue<T> components = new Queue<T>();
internal static Queue<T> newComponents = new Queue<T>();
internal static U system = null;
internal static void Setup(T component)
{
if (system == null)
{
var systems = Resources.FindObjectsOfTypeAll<U>();
if (systems.Length > 0)
system = systems[0];
else
{
system = new GameObject($"System:{typeof(T)}").AddComponent<U>();
}
}
if (!system.enabled)
system.enabled = true;
newComponents.Enqueue(component);
}
}
using UnityEngine;
public class CullableComponent : MonoBehaviourComponent<CullableComponent, CullableSystem, CullableJob>
{
public bool wasVisible;
public bool isVisible;
public Bounds bounds;
}
using System;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
[BurstCompile]
public struct CullableJob : IJobParallelFor
{
public NativeArray<bool> wasVisible, isVisible;
public NativeArray<float3> centers, extents;
public NativeArray<float4> frustum;
public void Execute(int index)
{
isVisible[index] = TestPlanesAABB(centers[index], extents[index]);
}
bool TestPlanesAABB(float3 center, float3 extent)
{
for (var i = 0; i < 6; i++)
{
var p = frustum[i];
float3 normal = p.xyz;
var dist = math.dot(normal, center) + p.w;
var radius = math.dot(extent, math.abs(normal));
if (dist + radius < 0)
return false;
}
return true;
}
}
public class CullableSystem : MonoBehaviourSystem<CullableComponent, CullableSystem, CullableJob>
{
public override CullableJob CreateJob(Queue<CullableComponent> components)
{
var job = new CullableJob();
var camera = Camera.main; //yeah yeah
job.frustum = new NativeArray<float4>(6, Allocator.TempJob);
var planes = GeometryUtility.CalculateFrustumPlanes(camera);
for (var i = 0; i < 6; i++)
{
var plane = (Vector4)planes[i].normal;
plane.w = planes[i].distance;
job.frustum[i] = plane;
}
var size = components.Count;
var wasVisible = job.wasVisible = new NativeArray<bool>(size, Allocator.TempJob);
var isVisible = job.isVisible = new NativeArray<bool>(size, Allocator.TempJob);
var centers = job.centers = new NativeArray<float3>(size, Allocator.TempJob);
var extents = job.extents = new NativeArray<float3>(size, Allocator.TempJob);
for (int i = 0, count = components.Count; i < count; i++)
{
var cullable = components.Dequeue();
components.Enqueue(cullable);
cullable.wasVisible = cullable.isVisible;
centers[i] = cullable.transform.position;
extents[i] = cullable.bounds.size;
}
return job;
}
public override void OnJobComplete(Queue<CullableComponent> components, CullableJob job)
{
for (int i = 0, count = components.Count; i < count; i++)
{
var cullable = components.Dequeue();
components.Enqueue(cullable);
cullable.isVisible = job.isVisible[i];
}
}
}
using Unity.Jobs;
using UnityEngine;
public abstract class MonoBehaviourComponent<T, U, V> : MonoBehaviour
where T : MonoBehaviourComponent<T, U, V>
where U : MonoBehaviourSystem<T, U, V>
where V: struct, IJobParallelFor
{
protected virtual void OnEnable()
{
ComponentBatch<T, U, V>.Setup((T)this);
}
}
using System.Collections.Generic;
using Unity.Jobs;
using UnityEngine;
public abstract class MonoBehaviourSystem<T, U, V> : MonoBehaviour
where T : MonoBehaviourComponent<T,U,V> where U : MonoBehaviourSystem<T, U,V>
where V: struct, IJobParallelFor
{
private JobHandle handle;
private V job;
public abstract V CreateJob(Queue<T> components);
public abstract void OnJobComplete(Queue<T> components, V job);
public virtual void InitComponent(T component)
{
}
private void Update()
{
while (ComponentBatch<T, U, V>.newComponents.Count > 0)
{
var component = ComponentBatch<T, U, V>.newComponents.Dequeue();
InitComponent(component);
ComponentBatch<T, U, V>.components.Enqueue(component);
}
var components = ComponentBatch<T, U, V>.components;
job = CreateJob(components);
handle = job.Schedule(components.Count, 24);
}
private void LateUpdate()
{
handle.Complete();
OnJobComplete(ComponentBatch<T, U, V>.components, job);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment