My Job System Tutorial listed many Unity APIs accessible from C# jobs, but the list was incomplete. Today I’ll add on to the list with some newly-released 2018.2 features as well as some powerful 2018.1 features that were left off of the last article. Many of these aren’t documented in Unity’s release notes. Read on to learn more about what you can do with C# jobs!

Update: A Russian translation of this article is available.

Thread-Safe Functions

As mentioned in the tutorial, many Unity functions are marked either with the [ThreadSafe] attribute or the [FreeFunction] attribute and IsThreadSafe set to true. The following is an updated list of all such functions. Each line marked with a * at the beginning has been added in Unity 2018.2:

*AnimationCurve:    private extern int AddKey_Internal_Injected(ref Keyframe key);
*AnimationCurve:    private extern Keyframe[] GetKeys();
*AnimationCurve:    private extern void SetKeys(Keyframe[] keys);
*AnimationCurve:    private int AddKey_Internal(Keyframe key)
*AnimationCurve:    private Keyframe GetKey(int index)
*AnimationCurve:    private static extern IntPtr Internal_Create(Keyframe[] keys);
*AnimationCurve:    private static extern void Internal_Destroy(IntPtr ptr);
*AnimationCurve:    public extern float Evaluate(float time);
*AnimationCurve:    public extern int AddKey(float time, float value);
*AnimationCurve:    public extern void RemoveKey(int index);
*AnimationCurve:    public extern void SmoothTangents(int index, float weight);
*AnimationCurve:    public int MoveKey(int index, Keyframe key)
*AnimationCurve:    public extern int length { get; }
*AnimationCurve:    public extern WrapMode postWrapMode { set; }
*AnimationCurve:    public extern WrapMode preWrapMode { set; }
*AnimationHumanStream:    private float GetFootHeight(bool left)
*AnimationHumanStream:    private float GetHumanScale()
*AnimationHumanStream:    private float InternalGetGoalWeightPosition(AvatarIKGoal index)
*AnimationHumanStream:    private float InternalGetGoalWeightRotation(AvatarIKGoal index)
*AnimationHumanStream:    private float InternalGetHintWeightPosition(AvatarIKHint index)
*AnimationHumanStream:    private float InternalGetMuscle(MuscleHandle muscle)
*AnimationHumanStream:    private Quaternion InternalGetBodyLocalRotation()
*AnimationHumanStream:    private Quaternion InternalGetBodyRotation()
*AnimationHumanStream:    private Quaternion InternalGetGoalLocalRotation(AvatarIKGoal index)
*AnimationHumanStream:    private Quaternion InternalGetGoalRotation(AvatarIKGoal index)
*AnimationHumanStream:    private Quaternion InternalGetGoalRotationFromPose(AvatarIKGoal index)
*AnimationHumanStream:    private Vector3 GetLeftFootVelocity()
*AnimationHumanStream:    private Vector3 GetRightFootVelocity()
*AnimationHumanStream:    private Vector3 InternalGetBodyLocalPosition()
*AnimationHumanStream:    private Vector3 InternalGetBodyPosition()
*AnimationHumanStream:    private Vector3 InternalGetGoalLocalPosition(AvatarIKGoal index)
*AnimationHumanStream:    private Vector3 InternalGetGoalPosition(AvatarIKGoal index)
*AnimationHumanStream:    private Vector3 InternalGetGoalPositionFromPose(AvatarIKGoal index)
*AnimationHumanStream:    private Vector3 InternalGetHintPosition(AvatarIKHint index)
*AnimationHumanStream:    private void InternalResetToStancePose()
*AnimationHumanStream:    private void InternalSetBodyLocalPosition(Vector3 value)
*AnimationHumanStream:    private void InternalSetBodyLocalRotation(Quaternion value)
*AnimationHumanStream:    private void InternalSetBodyPosition(Vector3 value)
*AnimationHumanStream:    private void InternalSetBodyRotation(Quaternion value)
*AnimationHumanStream:    private void InternalSetGoalLocalPosition(AvatarIKGoal index, Vector3 pos)
*AnimationHumanStream:    private void InternalSetGoalLocalRotation(AvatarIKGoal index, Quaternion rot)
*AnimationHumanStream:    private void InternalSetGoalPosition(AvatarIKGoal index, Vector3 pos)
*AnimationHumanStream:    private void InternalSetGoalRotation(AvatarIKGoal index, Quaternion rot)
*AnimationHumanStream:    private void InternalSetGoalWeightPosition(AvatarIKGoal index, float value)
*AnimationHumanStream:    private void InternalSetGoalWeightRotation(AvatarIKGoal index, float value)
*AnimationHumanStream:    private void InternalSetHintPosition(AvatarIKHint index, Vector3 pos)
*AnimationHumanStream:    private void InternalSetHintWeightPosition(AvatarIKHint index, float value)
*AnimationHumanStream:    private void InternalSetLookAtBodyWeight(float weight)
*AnimationHumanStream:    private void InternalSetLookAtClampWeight(float weight)
*AnimationHumanStream:    private void InternalSetLookAtEyesWeight(float weight)
*AnimationHumanStream:    private void InternalSetLookAtHeadWeight(float weight)
*AnimationHumanStream:    private void InternalSetLookAtPosition(Vector3 lookAtPosition)
*AnimationHumanStream:    private void InternalSetMuscle(MuscleHandle muscle, float value)
*AnimationHumanStream:    private void InternalSolveIK()
*AnimationStream:    private AnimationHumanStream GetHumanStream()
*AnimationStream:    private AnimationStream InternalGetInputStream(int index)
*AnimationStream:    private bool GetIsHumanStream()
*AnimationStream:    private float GetDeltaTime()
*AnimationStream:    private int GetInputStreamCount()
*AnimationStream:    private Quaternion GetRootMotionRotation()
*AnimationStream:    private Vector3 GetAngularVelocity()
*AnimationStream:    private Vector3 GetRootMotionPosition()
*AnimationStream:    private Vector3 GetVelocity()
*AnimationStream:    private void InternalReadSceneTransforms()
*AnimationStream:    private void InternalWriteSceneTransforms()
*AnimationStream:    private void SetAngularVelocity(Vector3 velocity)
*AnimationStream:    private void SetVelocity(Vector3 velocity)
Animator:    public static extern int StringToHash(string name);
AnimatorControllerPlayable:    private static extern int StringToHash(string name);
*Application:    public static extern RuntimePlatform platform { get; }
*AsyncOperation:    private static extern void InternalDestroy(IntPtr ptr);
*AtomicSafetyHandle:    internal static void CheckReadAndThrowNoEarlyOut(AtomicSafetyHandle handle)
*AtomicSafetyHandle:    internal static void CheckWriteAndThrowNoEarlyOut(AtomicSafetyHandle handle)
AtomicSafetyHandle:    public static AtomicSafetyHandle Create()
AtomicSafetyHandle:    public static AtomicSafetyHandle GetTempUnsafePtrSliceHandle()
*AtomicSafetyHandle:    public static bool GetAllowReadOrWriteAccess(AtomicSafetyHandle handle)
AtomicSafetyHandle:    public static EnforceJobResult EnforceAllBufferJobsHaveCompleted(AtomicSafetyHandle handle)
AtomicSafetyHandle:    public static EnforceJobResult EnforceAllBufferJobsHaveCompletedAndDisableReadWrite(AtomicSafetyHandle handle)
AtomicSafetyHandle:    public static EnforceJobResult EnforceAllBufferJobsHaveCompletedAndRelease(AtomicSafetyHandle handle)
*AtomicSafetyHandle:    public static extern void PrepareUndisposable(ref AtomicSafetyHandle handle);
AtomicSafetyHandle:    public static extern void UseSecondaryVersion(ref AtomicSafetyHandle handle);
AtomicSafetyHandle:    public static int GetReaderArray(AtomicSafetyHandle handle, int maxCount, IntPtr output)
*AtomicSafetyHandle:    public static JobHandle GetWriter(AtomicSafetyHandle handle)
AtomicSafetyHandle:    public static string GetReaderName(AtomicSafetyHandle handle, int readerIndex)
AtomicSafetyHandle:    public static string GetWriterName(AtomicSafetyHandle handle)
*AtomicSafetyHandle:    public static void CheckDeallocateAndThrow(AtomicSafetyHandle handle)
*AtomicSafetyHandle:    public static void CheckGetSecondaryDataPointerAndThrow(AtomicSafetyHandle handle)
*AtomicSafetyHandle:    public static void CheckWriteAndBumpSecondaryVersion(AtomicSafetyHandle handle)
AtomicSafetyHandle:    public static void Release(AtomicSafetyHandle handle)
AtomicSafetyHandle:    public static void SetAllowReadOrWriteAccess(AtomicSafetyHandle handle, bool allowReadWriteAccess)
AtomicSafetyHandle:    public static void SetAllowSecondaryVersionWriting(AtomicSafetyHandle handle, bool allowWriting)
AudioSampleProvider:    private static extern bool InternalGetEnableSampleFramesAvailableEvents(uint providerId);
AudioSampleProvider:    private static extern bool InternalGetEnableSilencePadding(uint id);
AudioSampleProvider:    private static extern bool InternalIsValid(uint providerId);
*AudioSampleProvider:    private static extern IntPtr InternalGetConsumeSampleFramesNativeFunctionPtr();
AudioSampleProvider:    private static extern uint InternalGetAvailableSampleFrameCount(uint providerId);
AudioSampleProvider:    private static extern uint InternalGetFreeSampleFrameCount(uint providerId);
AudioSampleProvider:    private static extern uint InternalGetFreeSampleFrameCountLowThreshold(uint providerId);
AudioSampleProvider:    private static extern uint InternalGetMaxSampleFrameCount(uint providerId);
AudioSampleProvider:    private static extern void InternalSetEnableSampleFramesAvailableEvents(uint providerId, bool enable);
AudioSampleProvider:    private static extern void InternalSetEnableSilencePadding(uint id, bool enabled);
AudioSampleProvider:    private static extern void InternalSetFreeSampleFrameCountLowThreshold(uint providerId, uint sampleFrameCount);
AudioSampleProvider:    private static extern void InternalSetScriptingPtr(uint providerId, AudioSampleProvider provider);
Bounds:    private static bool IntersectRayAABB(Ray ray, Bounds bounds, out float dist)
Bounds:    public bool Contains(Vector3 point)
Bounds:    public float SqrDistance(Vector3 point)
*Bounds:    public Vector3 ClosestPoint(Vector3 point)
CertificateHandler:    private extern void Release();
*CullingGroup:    private extern void FinalizerFailure();
CustomEventData:    internal static extern void Internal_Destroy(IntPtr ptr);
*Device:    private static extern void SettvOSNoBackupFlag(string path);
*Device:    private static extern void tvOSResetNoBackupFlag(string path);
Device:    public static extern bool RequestStoreReview();
Device:    public static extern void ResetNoBackupFlag(string path);
Device:    public static extern void SetNoBackupFlag(string path);
*DictationRecognizer:    private static extern void DestroyThreaded(IntPtr self);
DownloadHandler:    private extern void Release();
*Event:    internal extern void CopyFromPtr(IntPtr ptr);
*Event:    private static extern IntPtr Internal_Copy(IntPtr otherPtr);
*Event:    private static extern IntPtr Internal_Create(int displayIndex);
*Event:    private static extern void Internal_Destroy(IntPtr ptr);
GcLeaderboard:    private static extern void GcLeaderboard_Dispose(IntPtr leaderboard);
*Gradient:    private extern void Cleanup();
*Gradient:    private static extern IntPtr Init();
*Gradient:    public Color Evaluate(float time)
*Gradient:    public extern GradientMode mode { get; set; }
*Gradient:    public extern void SetKeys(GradientColorKey[] colorKeys, GradientAlphaKey[] alphaKeys);
*Gradient:    public extern GradientAlphaKey[] alphaKeys { get; set; }
*Gradient:    public extern GradientColorKey[] colorKeys { get; set; }
*GUIStyle:    private extern IntPtr GetStyleStatePtr(int idx);
*GUIStyle:    private static extern IntPtr Internal_Copy(GUIStyle self, GUIStyle other);
*GUIStyle:    private static extern IntPtr Internal_Create(GUIStyle self);
*GUIStyle:    private static extern void Internal_Destroy(IntPtr self);
*GUIStyleState:    private extern void Cleanup();
*GUIStyleState:    private static extern IntPtr Init();
JobsUtility:    public static extern bool GetWorkStealingRange(ref JobRanges ranges, int jobIndex, out int beginIndex, out int endIndex);
JobsUtility:    public static extern unsafe void PatchBufferMinMaxRanges(IntPtr bufferRangePatchData, void* jobdata, int startIndex, int rangeSize);
*JsonUtility:    private static extern object FromJsonInternal(string json, object objectToOverwrite, System.Type type);
*JsonUtility:    private static extern string ToJsonInternal(object obj, bool prettyPrint);
MaterialPropertyBlock:    private static extern void DestroyImpl(IntPtr mpb);
Mathf:    public static Color CorrelatedColorTemperatureToRGB(float kelvin)
Mathf:    public static extern bool IsPowerOfTwo(int value);
Mathf:    public static extern float GammaToLinearSpace(float value);
Mathf:    public static extern float HalfToFloat(ushort val);
Mathf:    public static extern float LinearToGammaSpace(float value);
Mathf:    public static extern float PerlinNoise(float x, float y);
Mathf:    public static extern int ClosestPowerOfTwo(int value);
Mathf:    public static extern int NextPowerOfTwo(int value);
Mathf:    public static extern ushort FloatToHalf(float val);
*Matrix4x4:    private bool IsIdentity()
*Matrix4x4:    private float GetDeterminant()
Matrix4x4:    private FrustumPlanes DecomposeProjection()
Matrix4x4:    private Quaternion GetRotation()
Matrix4x4:    private Vector3 GetLossyScale()
Matrix4x4:    public bool ValidTRS()
Matrix4x4:    public static Matrix4x4 Frustum(float left, float right, float bottom, float top, float zNear, float zFar)
Matrix4x4:    public static Matrix4x4 Inverse(Matrix4x4 m)
Matrix4x4:    public static Matrix4x4 LookAt(Vector3 from, Vector3 to, Vector3 up)
Matrix4x4:    public static Matrix4x4 Ortho(float left, float right, float bottom, float top, float zNear, float zFar)
Matrix4x4:    public static Matrix4x4 Perspective(float fov, float aspect, float zNear, float zFar)
Matrix4x4:    public static Matrix4x4 Transpose(Matrix4x4 m)
*Matrix4x4:    public static Matrix4x4 TRS(Vector3 pos, Quaternion q, Vector3 s)
MeshReconstructionServer:    internal static extern void DestroyThreaded(IntPtr server);
MonoBehaviour:    private static extern void ConstructorCheck(Object self);
*Native:    private static extern void YGConfigFreeInternal(IntPtr config);
*Native:    private static extern void YGNodeFreeInternal(IntPtr ygNode);
NavMeshQuery:    private static bool GetPortalPoints(IntPtr navMeshQuery, PolygonId polygon, PolygonId neighbourPolygon, out Vector3 left, out Vector3 right)
NavMeshQuery:    private static bool IsPositionInPolygon(IntPtr navMeshQuery, Vector3 position, PolygonId polygon)
NavMeshQuery:    private static bool IsValidPolygon(IntPtr navMeshQuery, PolygonId polygon)
*NavMeshQuery:    private static extern bool HasNodePool(IntPtr navMeshQuery);
NavMeshQuery:    private static extern PathQueryStatus EndFindPath(IntPtr navMeshQuery, out int pathSize);
NavMeshQuery:    private static extern PathQueryStatus UpdateFindPath(IntPtr navMeshQuery, int iterations, out int iterationsPerformed);
NavMeshQuery:    private static extern unsafe int GetPathResult(IntPtr navMeshQuery, void* path, int maxPath);
NavMeshQuery:    private static extern unsafe void MoveLocations(IntPtr navMeshQuery, void* locations, void* targets, void* areaMasks, int count);
NavMeshQuery:    private static extern unsafe void MoveLocationsInSameAreas(IntPtr navMeshQuery, void* locations, void* targets, int count, int areaMask);
*NavMeshQuery:    private static int GetAgentTypeIdForPolygon(IntPtr navMeshQuery, PolygonId polygon)
*NavMeshQuery:    private static Matrix4x4 PolygonLocalToWorldMatrix(IntPtr navMeshQuery, PolygonId polygon)
*NavMeshQuery:    private static Matrix4x4 PolygonWorldToLocalMatrix(IntPtr navMeshQuery, PolygonId polygon)
*NavMeshQuery:    private static NavMeshLocation MapLocation(IntPtr navMeshQuery, Vector3 position, Vector3 extents, int agentTypeID, int areaMask = -1)
*NavMeshQuery:    private static NavMeshLocation MoveLocation(IntPtr navMeshQuery, NavMeshLocation location, Vector3 target, int areaMask)
*NavMeshQuery:    private static NavMeshPolyTypes GetPolygonType(IntPtr navMeshQuery, PolygonId polygon)
*NavMeshQuery:    private static PathQueryStatus GetClosestPointOnPoly(IntPtr navMeshQuery, PolygonId polygon, Vector3 position, out Vector3 nearest)
*NavMeshQuery:    private static unsafe PathQueryStatus BeginFindPath(IntPtr navMeshQuery, NavMeshLocation start, NavMeshLocation end, int areaMask, void* costs)
NotificationHelper:    internal static extern void DestroyLocal(IntPtr target);
NotificationHelper:    internal static extern void DestroyRemote(IntPtr target);
Object:    internal static extern bool DoesObjectWithInstanceIDExist(int instanceID);
Object:    private static extern bool CurrentThreadIsMainThread();
Object:    private static extern int GetOffsetOfInstanceIDInCPlusPlusObject();
ObjectGUIState:    private static extern void Internal_Destroy(IntPtr ptr);
OnDemandResourcesRequest:    private static extern void DestroyFromScript(IntPtr ptr);
*PhraseRecognizer:    private static extern void DestroyThreaded(IntPtr recognizer);
Ping:    private static extern void Internal_Destroy(IntPtr ptr);
*PropertySceneHandle:    private bool GetBoolInternal(ref AnimationStream stream)
*PropertySceneHandle:    private bool HasValidTransform(ref AnimationStream stream)
*PropertySceneHandle:    private bool IsBound(ref AnimationStream stream)
*PropertySceneHandle:    private float GetFloatInternal(ref AnimationStream stream)
*PropertySceneHandle:    private int GetIntInternal(ref AnimationStream stream)
*PropertySceneHandle:    private void ResolveInternal(ref AnimationStream stream)
*PropertySceneHandle:    private void SetBoolInternal(ref AnimationStream stream, bool value)
*PropertySceneHandle:    private void SetFloatInternal(ref AnimationStream stream, float value)
*PropertySceneHandle:    private void SetIntInternal(ref AnimationStream stream, int value)
*PropertyStreamHandle:    private bool GetBoolInternal(ref AnimationStream stream)
*PropertyStreamHandle:    private float GetFloatInternal(ref AnimationStream stream)
*PropertyStreamHandle:    private int GetIntInternal(ref AnimationStream stream)
*PropertyStreamHandle:    private void ResolveInternal(ref AnimationStream stream)
*PropertyStreamHandle:    private void SetBoolInternal(ref AnimationStream stream, bool value)
*PropertyStreamHandle:    private void SetFloatInternal(ref AnimationStream stream, float value)
*PropertyStreamHandle:    private void SetIntInternal(ref AnimationStream stream, int value)
Quaternion:    private static Quaternion Internal_FromEulerRad(Vector3 euler)
Quaternion:    private static Vector3 Internal_ToEulerRad(Quaternion rotation)
Quaternion:    private static void Internal_ToAxisAngleRad(Quaternion q, out Vector3 axis, out float angle)
Quaternion:    public static Quaternion AngleAxis(float angle, Vector3 axis)
Quaternion:    public static Quaternion FromToRotation(Vector3 fromDirection, Vector3 toDirection)
Quaternion:    public static Quaternion Inverse(Quaternion rotation)
Quaternion:    public static Quaternion Lerp(Quaternion a, Quaternion b, float t)
Quaternion:    public static Quaternion LerpUnclamped(Quaternion a, Quaternion b, float t)
Quaternion:    public static Quaternion LookRotation(Vector3 forward, Vector3 upwards)
Quaternion:    public static Quaternion Slerp(Quaternion a, Quaternion b, float t)
Quaternion:    public static Quaternion SlerpUnclamped(Quaternion a, Quaternion b, float t)
RemoteConfigSettings:    internal static extern void Internal_Destroy(IntPtr ptr);
*Screen:    public static extern float dpi { get; }
Screen:    public static extern int height { get; }
Screen:    public static extern int width { get; }
ScriptableObject:    private static extern void CreateScriptableObject(ScriptableObject self);
*Shader:    public static extern int PropertyToID(string name);
*StylePainter:    private static extern IntPtr Internal_Create();
*StylePainter:    private static extern void Internal_Destroy(IntPtr self);
*TerrainData:    private static extern int GetBoundaryValue(TerrainData.BoundaryValueType type);
*TextGenerator:    private static extern IntPtr Internal_Create();
*TextGenerator:    private static extern void Internal_Destroy(IntPtr ptr);
*TouchScreenKeyboard:    private static extern void Internal_Destroy(IntPtr ptr);
TransformAccess:    private static extern void GetLocalPosition(ref TransformAccess access, out Vector3 p);
TransformAccess:    private static extern void GetLocalRotation(ref TransformAccess access, out Quaternion r);
TransformAccess:    private static extern void GetLocalScale(ref TransformAccess access, out Vector3 r);
TransformAccess:    private static extern void GetPosition(ref TransformAccess access, out Vector3 p);
TransformAccess:    private static extern void GetRotation(ref TransformAccess access, out Quaternion r);
TransformAccess:    private static extern void SetLocalPosition(ref TransformAccess access, ref Vector3 p);
TransformAccess:    private static extern void SetLocalRotation(ref TransformAccess access, ref Quaternion r);
TransformAccess:    private static extern void SetLocalScale(ref TransformAccess access, ref Vector3 r);
TransformAccess:    private static extern void SetPosition(ref TransformAccess access, ref Vector3 p);
TransformAccess:    private static extern void SetRotation(ref TransformAccess access, ref Quaternion r);
TransformAccessArray:    internal static extern IntPtr GetSortedToUserIndex(IntPtr transformArrayIntPtr);
TransformAccessArray:    internal static extern IntPtr GetSortedTransformAccess(IntPtr transformArrayIntPtr);
*TransformSceneHandle:    private bool HasValidTransform(ref AnimationStream stream)
*TransformSceneHandle:    private Quaternion GetLocalRotationInternal(ref AnimationStream stream)
*TransformSceneHandle:    private Quaternion GetRotationInternal(ref AnimationStream stream)
*TransformSceneHandle:    private Vector3 GetLocalPositionInternal(ref AnimationStream stream)
*TransformSceneHandle:    private Vector3 GetLocalScaleInternal(ref AnimationStream stream)
*TransformSceneHandle:    private Vector3 GetPositionInternal(ref AnimationStream stream)
*TransformSceneHandle:    private void SetLocalPositionInternal(ref AnimationStream stream, Vector3 position)
*TransformSceneHandle:    private void SetLocalRotationInternal(ref AnimationStream stream, Quaternion rotation)
*TransformSceneHandle:    private void SetLocalScaleInternal(ref AnimationStream stream, Vector3 scale)
*TransformSceneHandle:    private void SetPositionInternal(ref AnimationStream stream, Vector3 position)
*TransformSceneHandle:    private void SetRotationInternal(ref AnimationStream stream, Quaternion rotation)
*TransformStreamHandle:    private Quaternion GetLocalRotationInternal(ref AnimationStream stream)
*TransformStreamHandle:    private Quaternion GetRotationInternal(ref AnimationStream stream)
*TransformStreamHandle:    private Vector3 GetLocalPositionInternal(ref AnimationStream stream)
*TransformStreamHandle:    private Vector3 GetLocalScaleInternal(ref AnimationStream stream)
*TransformStreamHandle:    private Vector3 GetPositionInternal(ref AnimationStream stream)
*TransformStreamHandle:    private void ResolveInternal(ref AnimationStream stream)
*TransformStreamHandle:    private void SetLocalPositionInternal(ref AnimationStream stream, Vector3 position)
*TransformStreamHandle:    private void SetLocalRotationInternal(ref AnimationStream stream, Quaternion rotation)
*TransformStreamHandle:    private void SetLocalScaleInternal(ref AnimationStream stream, Vector3 scale)
*TransformStreamHandle:    private void SetPositionInternal(ref AnimationStream stream, Vector3 position)
*TransformStreamHandle:    private void SetRotationInternal(ref AnimationStream stream, Quaternion rotation)
UnityAnalytHandler:    internal static extern void Internal_Destroy(IntPtr ptr);
UnityLogWriter:    private static extern void WriteStringToUnityLogImpl(string s);
UnityWebRequest:    private extern void Release();
UnityWebRequest:    private static extern string GetWebErrorString(UnityWebRequest.UnityWebRequestError err);
UnityWebRequest:    public extern void Abort();
UnsafeUtility:    internal static extern void LogError(string msg, string filename, int linenumber);
UnsafeUtility:    private static extern int GetFieldOffsetInClass(FieldInfo field);
UnsafeUtility:    private static extern int GetFieldOffsetInStruct(FieldInfo field);
UnsafeUtility:    public static extern bool IsBlittable(Type type);
UnsafeUtility:    public static extern int SizeOf(Type type);
*UnsafeUtility:    public static extern unsafe int MemCmp(void* ptr1, void* ptr2, long size);
UnsafeUtility:    public static extern unsafe void CopyObjectAddressToPtr(object target, void* dstPtr);
UnsafeUtility:    public static extern unsafe void Free(void* memory, Allocator allocator);
UnsafeUtility:    public static extern unsafe void MemClear(void* destination, long size);
UnsafeUtility:    public static extern unsafe void MemCpy(void* destination, void* source, long size);
UnsafeUtility:    public static extern unsafe void MemCpyReplicate(void* destination, void* source, int size, int count);
UnsafeUtility:    public static extern unsafe void MemCpyStride(void* destination, int destinationStride, void* source, int sourceStride, int elementSize, int count);
UnsafeUtility:    public static extern unsafe void MemMove(void* destination, void* source, long size);
UnsafeUtility:    public static extern unsafe void* Malloc(long size, int alignment, Allocator allocator);
UnsafeUtility:    public static extern unsafe void* PinGCObjectAndGetAddress(object target, out ulong gcHandle);
UnsafeUtility:    public static extern void ReleaseGCObject(ulong gcHandle);
UploadHandler:    private extern void Release();
Vector3:    private static extern void OrthoNormalize2(ref Vector3 a, ref Vector3 b);
Vector3:    private static extern void OrthoNormalize3(ref Vector3 a, ref Vector3 b, ref Vector3 c);
Vector3:    public static Vector3 RotateTowards(Vector3 current, Vector3 target, float maxRadiansDelta, float maxMagnitudeDelta)
Vector3:    public static Vector3 Slerp(Vector3 a, Vector3 b, float t)
Vector3:    public static Vector3 SlerpUnclamped(Vector3 a, Vector3 b, float t)

A lot of miscellaneous new functions are now accessible from C# jobs in 2018.2. Many are of limited usefulness in jobs either due to their nature or the fact that they require reference types like classes or arrays, which are forbidden. Some are quite handy though. Here are some highlights:

  • Bounds.ClosestPoint: useful for logic based on 3D geometry since Physics is off-limits
  • JsonUtility.FromJsonInternal and ToJsonInternal: JSON serialization and deserialization can now be performed off the main thread
  • Matrix4x4.IsIdentity and GetDeterminant: helpful functions when dealing with matrices in a job
  • UnsafeUtility.MemCmp: efficiently compare two blocks of memory
Animation C# Jobs

The majority of the functions above that were added to support the new Animation C# Jobs feature which allows for jobs to manipulate animations and transforms. This accounts for AnimationHumanStream, AnimationStream, PropertySceneHandle, and arguably TransformSceneHandle and TransformStreamHandle. This a deep topic beyond the scope of this article, so check out Unity’s samples for how to use this feature and some ideas about what to do with it.

NavMeshQuery

Another big chunk of the newly-added functions above are internal changes to the “experimental” NavMeshQuery struct. Together with its related NavMeshWorld, PolygonId, and NavMeshLocation structs, this feature allows for querying the nav mesh and pathfinding from within C# jobs. As these can be very CPU-intensive operations, it’s great to be able to move them off the main thread and into jobs. This feature existed in 2018.1 and continues to be “experimental” in 2018.2. The Unity Roadmap is currently silent about when this feature will be released in stable form.

RaycastCommand

Not listed above and not mentioned in the 2018.1 release notes is RaycastCommand. Regardless, this was added in 2018.1 and allows us to create jobs that cast a batch of rays and output their results to a NativeArray<RayHit>. While Physics is generally inaccessible off the main thread, including from jobs, at least raycasting is supported as it’s one of the most common physics operations used by everything from gun shots to movement logic. Eventually, Unity wants to add more of these physics commands as well as supporting 2D physics.

Conclusion

More and more functionality is made available to C# jobs with each release of Unity. Support for raycasting in 2018.1 and animation and JSON in 2018.2 allow us to move heavy-lifting off the main thread that was never possible in previous versions of Unity. Stable nav mesh support and additional physics operations are on the horizon, as is the ECS. As these are released, more and more code can be converted from the old APIs to the new jobs-based approach. As we do this, we gain tremendous performance increases and energy-usage reductions. Let me know in the comments what you think about this paradigm shift.