Skip to content

Instantly share code, notes, and snippets.

@runevision
Last active April 5, 2025 08:05
Show Gist options
  • Save runevision/e25310fbdd88a90a4a6a67fff69cb16d to your computer and use it in GitHub Desktop.
Save runevision/e25310fbdd88a90a4a6a67fff69cb16d to your computer and use it in GitHub Desktop.
Using Unity Burst directly on methods without jobs - unofficial example code
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEngine;
// This example code demonstrates using Unity Burst directly on a static method without jobs.
// Unity NativeArrays can't be used directly in Unity Burst methods (only in Burst jobs),
// so we have to pass pointers to arrays instead.
//
// This PointerArray wrapper can be created from a NativeArray via simple implicit assignment:
//
// NativeArray<float> floatNativeArray = new NativeArray<float>(1000, Allocator.Persistent);
// PointerArray<float> floatPointerArray = floatNativeArray;
//
// The PointerArray can then be used like an array itself, including getting the Length.
public unsafe struct PointerArray<T> where T : unmanaged {
public readonly T* Array;
public readonly int Length;
public T this[uint index] {
get { return Array[index]; }
set { Array[index] = value; }
}
public T this[int index] {
get { return Array[index]; }
set { Array[index] = value; }
}
public PointerArray(T* Array, int Length) {
this.Array = Array;
this.Length = Length;
}
public static implicit operator PointerArray<T>(NativeArray<T> a) {
return new PointerArray<T>((T*)a.GetUnsafePtr(), a.Length);
}
}
[BurstCompile]
public class BurstMethodTester : MonoBehaviour {
enum Mode { NonBurstMethod, NonBurstJob, BurstMethod, BurstJob }
void Start() {
Execute(Mode.NonBurstMethod);
Execute(Mode.NonBurstJob);
Execute(Mode.BurstMethod);
Execute(Mode.BurstJob);
}
void Execute(Mode mode) {
var input = new NativeArray<float>(10000000, Allocator.Persistent);
var output = new NativeArray<float>(1, Allocator.Persistent);
for (int i = 0; i < input.Length; i++)
input[i] = 1.0f * i;
var stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
if (mode == Mode.NonBurstMethod) {
PointerArray<float> outputArr = output;
ExecuteNonBurstMethod(input, ref outputArr);
}
else if (mode == Mode.NonBurstJob) {
var job = new MyNonBurstStruct { Input = input, Output = output };
job.Schedule().Complete();
}
else if (mode == Mode.BurstMethod) {
PointerArray<float> outputArr = output;
ExecuteBurstMethod(input, ref outputArr);
}
else if (mode == Mode.BurstJob) {
var job = new MyBurstStruct { Input = input, Output = output };
job.Schedule().Complete();
}
stopwatch.Stop();
Debug.Log("Took " + stopwatch.ElapsedMilliseconds + "ms with mode " + mode
+ "\nThe result of the sum is: " + output[0]);
input.Dispose();
output.Dispose();
}
[BurstCompile]
public static void ExecuteBurstMethod(in PointerArray<float> Input, ref PointerArray<float> Output) {
float result = 0.0f;
for (int i = 0; i < Input.Length; i++) {
result += Input[i];
}
Output[0] = result;
}
public static void ExecuteNonBurstMethod(in PointerArray<float> Input, ref PointerArray<float> Output) {
float result = 0.0f;
for (int i = 0; i < Input.Length; i++) {
result += Input[i];
}
Output[0] = result;
}
[BurstCompile(CompileSynchronously = true)]
private struct MyBurstStruct : IJob {
[ReadOnly]
public NativeArray<float> Input;
[WriteOnly]
public NativeArray<float> Output;
public void Execute() {
float result = 0.0f;
for (int i = 0; i < Input.Length; i++) {
result += Input[i];
}
Output[0] = result;
}
}
private struct MyNonBurstStruct : IJob {
[ReadOnly]
public NativeArray<float> Input;
[WriteOnly]
public NativeArray<float> Output;
public void Execute() {
float result = 0.0f;
for (int i = 0; i < Input.Length; i++) {
result += Input[i];
}
Output[0] = result;
}
}
}
@runevision
Copy link
Author

BurstMethod and BurstJob perform around 10x of NonBurstMethod and NonBurstJob. Times can vary a lot from one run to the next.

Job vs Method makes no difference here as there's only one job. But the Job version was the original code from Unity's documentation. I adapted the Method version from that and kept the Job version too so I could check if the Method version got the same speed boost or not.

@PaperPrototype
Copy link

What version of Unity did you have to use for this to work? I remember trying to get something like this working and founding out (to my disappointment) that it was not possible. When did this become possible ( •̯́ ^ •̯̀)

@runevision
Copy link
Author

What version of Unity did you have to use for this to work?

I was using this in Unity 2019.4 and later. I don't know in which version it started working.

@AndrzejKebab
Copy link

What version of Unity did you have to use for this to work?

Burst has always been available for static methods.

And I don't see any need for PointerArray<T> to be required for a method to use NativeArray<T> with burst.
Like I did

[BurstCompile]
private static void CompleteBurst(ref NativeArray<JobHandle> jobHandles, byte batchSize)
[BurstCompile]
private static void ScheduleBurst(ref NativeArray<JobForData<T>> jobArray, ref JobSchedulerBase baseScheduler, byte batchSize)

and it worked.
I may have misunderstood something.

@runevision
Copy link
Author

I don't see any need for PointerArray<T> to be required for a method to use NativeArray<T> with burst.

It's needed in Unity 2019.4 where it's not possible to pass NativeArrays to Burst-compiled methods. In some later version it became possible (I can't remember which version).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment