JobSystemおべんきょう
Unity JobSystem
テラシュールブログ様の写経
JobSystemは並列処理らしい。
- 並列処理が簡単にできるぞ的な。
- あと書き方間違ってたらエラー吐いてくれる(優しい)。
- 値型のみ使える。
- 参照型は使えない。
NativeArrayなるもの。
- NativeArrayは、ネイティブメモリのバッファをマネージコードに公開し、マネージドとネイティブの間でデータを共有することを可能にします。(公式 with Google翻訳)
- 速いらしい。
- 要素をあとから増やせない。
- 構造体structのみ使える。
- クラスclassは使えない。
- Allocator(メモリの割当タイプ)を自分で決める必要がある。
- Temp 割当と開放が速いらしい。1F内で開放する必要がある。
- Persistent 割当と開放が遅いらしい。永続的な割当。Destroy,Disable等で開放する。
使い終わったら必ず開放する必要がある。.Dispose();
- メモリリーク起こす(Unityがエラーとか警告吐いてくれる(優しい))
TransformAccessArrayなるものもある。
- 通常のComponentと違い、参照型なのにJobSystemで扱える。
以下宣言から開放のサンプル。
using System.Linq; using UnityEngine; using Unity.Collections; public class JobSystemTest : MonoBehaviour { TransformAccessArray targets; NativeArray<RaycastCommand> commands; int length = 10; void OnEnable() { var targetList = new List<Transform>(); targets = new TransformAccessArray(targetList.ToArray()) commands = new NativeArray<RaycastCommand>(length, Allocator.Persistent); } void Update() { var results = new NativeArray<RaycastHit>(length, Allocator.Temp); //--- //いろいろやる //---- results.Dispose(); } void OnDisable() { commands.Dispose(); targets.Dispose(); } }
IJobParallelForなるもの
- これを継承した構造体のExecute()内に記述した処理が、各ジョブで実行される。
- IJobParallelForTransformなるものもある
JobHandleなるもの
- JobHandle。(公式 with Google翻訳)
- IJobParallelForを継承した構造体.Schedule()の返り値がこれ。
- .Complete()でジョブの完了を待つ。
- .Schedule()の第三引数にJobHandleを登録すると、登録したJobHandleの完了を待ってから実行。
- RaycastCommand.ScheduleBatch()なるものもある。
void RandomBounce() { for(int i = 0; i < targetCount; i++) { bounce[i] = Random.Range(0.5f, 2.5f); } } void CreateJobs() { handle.Complete(); for(int i = 0; i < targetCount; i++) { commands[i] = new RaycastCommand(targets[i].position, Vector3.down); } UpdatePosition updatePositionJob = new UpdatePosition { raycastResults = results, velocitys = velocity, bounces = bounce }; ApplyPosition applyPosition = new ApplyPosition { velocitys = velocity }; var raycastJobHandle = RaycastCommand.ScheduleBatch(commands, results, Mathf.FloorToInt(targetCount / 3)); var updatePositionHandle = updatePositionJob.Schedule(targetCount, Mathf.FloorToInt(targetCount / 3), raycastJobHandle); handle = applyPosition.Schedule(targets, updatePositionHandle); } struct UpdatePosition : IJobParallelFor { [ReadOnly] public NativeArray<RaycastHit> raycastResults; public NativeArray<float> velocitys; public NativeArray<float> bounces; void IJobParallelFor.Execute(int index) { if(velocitys[index] < 0 && raycastResults[index].distance < 0.5f) { velocitys[index] = bounces[index];//Random.Rangeとかは使えない } velocitys[index] -= gravity; } } struct ApplyPosition : IJobParallelForTransform { public NativeArray<float> velocitys; void IJobParallelForTransform.Execute(int index, TransformAccess transform) { transform.localPosition += Vector3.up * velocitys[index]; } }
[ReadOnly]が以外と大事だったりするのでまた書かないとならない。
成果物。
using System.Linq; using UnityEngine; using UnityEngine.Jobs; using Unity.Collections; using Unity.Jobs; public class JobSystemTest : MonoBehaviour { [SerializeField] CanvasGroup canvasGroup; Transform[] transforms; NativeArray<RaycastCommand> commands; NativeArray<RaycastHit> results; NativeArray<float> velocity; NativeArray<float> bounce; TransformAccessArray targets; const int RaycastDistance = 130; const float gravity = 0.098f; int targetCount; JobHandle handle; void OnEnable() { var targetList = GetComponentsInChildren<Transform>().ToList(); targetList.Remove(transform); targetCount = targetList.Count; foreach(var target in targetList) { target.localPosition = new Vector3(Random.Range(5, 45), 4, Random.Range(5, 45)); } commands = new NativeArray<RaycastCommand>(targetCount, Allocator.Persistent); results = new NativeArray<RaycastHit>(targetCount, Allocator.Persistent); velocity = new NativeArray<float>(targetCount, Allocator.Persistent); for(int i = 0; i < targetCount; i++) { velocity[i] = -1.0f; } bounce = new NativeArray<float>(targetCount, Allocator.Persistent); targets = new TransformAccessArray(targetList.ToArray()); } void OnDisable() { handle.Complete(); commands.Dispose(); results.Dispose(); velocity.Dispose(); bounce.Dispose(); targets.Dispose(); } void Update() { RandomBounce(); CreateJobs(); } void RandomBounce() { for(int i = 0; i < targetCount; i++) { bounce[i] = Random.Range(0.5f, 2.5f); } } void CreateJobs() { handle.Complete(); for(int i = 0; i < targetCount; i++) { commands[i] = new RaycastCommand(targets[i].position, Vector3.down); } UpdatePosition updatePositionJob = new UpdatePosition { raycastResults = results, velocitys = velocity, bounces = bounce }; ApplyPosition applyPosition = new ApplyPosition { velocitys = velocity }; HitCheck hitCheckJob = new HitCheck { raycastResults = results, result = new NativeArray<int>(1, Allocator.TempJob) }; var raycastJobHandle = RaycastCommand.ScheduleBatch(commands, results, Mathf.FloorToInt(targetCount / 3)); var isHitJobHandle = hitCheckJob.Schedule(raycastJobHandle); var updatePositionHandle = updatePositionJob.Schedule(targetCount, Mathf.FloorToInt(targetCount / 3), raycastJobHandle); handle = applyPosition.Schedule(targets, updatePositionHandle); isHitJobHandle.Complete(); canvasGroup.alpha = hitCheckJob.result[0]; hitCheckJob.result.Dispose(); } struct UpdatePosition : IJobParallelFor { [ReadOnly] public NativeArray<RaycastHit> raycastResults; public NativeArray<float> velocitys; public NativeArray<float> bounces; void IJobParallelFor.Execute(int index) { if(velocitys[index] < 0 && raycastResults[index].distance < 0.5f) { velocitys[index] = bounces[index];//Random.Rangeとかは使えない } velocitys[index] -= gravity; } } struct ApplyPosition : IJobParallelForTransform { public NativeArray<float> velocitys; void IJobParallelForTransform.Execute(int index, TransformAccess transform) { transform.localPosition += Vector3.up * velocitys[index]; } } struct HitCheck : IJob { [ReadOnly] public NativeArray<RaycastHit> raycastResults; public NativeArray<int> result; void IJob.Execute() { foreach(var raycastResult in raycastResults) { if(raycastResult.distance < 1.0f) { result[0] = 1; return; } } result[0] = 0; } } }