2.6K Views
April 23, 18
スライド概要
Unite Tokyo 2018 Training Day「C#JobSystem & ECSでCPUを極限まで使い倒そう ~C# JobSystem 編~」の資料です。
講師:黒河 優介(ディベロッパーリレーションエンジニア|ユニティ・テクノロジーズ・ジャパン合同会社)
※【Unite Tokyo 2018 Training Day】C#JobSystem & ECSでCPUを極限まで使い倒そう ~Entity Component System 編~ の資料はこちら
https://www.slideshare.net/UnityTechnologiesJapan/unite-tokyo-2018-training-daycjobsystem-ecscpu-entity-component-system-1
■ワークショップで利用したUnityプロジェクト
https://wotakuro.github.io/JobSystemWorkshop/JobSystemWorkshop.zip
■ワークショップ内容
C# Job System、ECS(Entity Component System)がUnity2018で使えるようになりました。
C# Job SystemはUnityのシステムと親和性が高く、安全なマルチスレッドプログラミングを容易にする新機能です。ECSは非常に高速な、新しいオブジェクトの管理システムです。この二つを組み合わせて、CPUのパワーを非常に効率よく利用することができます。
※このセッションは、受講者がUnity上での基本的なC#プログラミング・マルチスレッドプログラミングに関する基本的な知識を持っている前提で行います。
※ECSは現在ベータ段階で今後も調整、変更が重ねられる予定です。
リアルタイム3Dコンテンツを制作・運用するための世界的にリードするプラットフォームである「Unity」の日本国内における販売、サポート、コミュニティ活動、研究開発、教育支援を行っています。ゲーム開発者からアーティスト、建築家、自動車デザイナー、映画製作者など、さまざまなクリエイターがUnityを使い想像力を発揮しています。
2018/4/21 C#JobSystem & ECSでCPUを極限まで 使い倒そう ~C# JobSystem編~ 講演者名 黒河 優介 所属団体 Unity Technologies Japan 肩書・役職 Developer Relation Engineer
넝鸞⻉➙ךך䖓ך堣腉גְאח • Unityד٦ي⡲דְֲִֻגծءوٝך䚍腉剑㣐ꣲ涪䳸ׅ חծ㣐ֹֻ4ךא堣腉ָ➙䖓ٔٔ٦ׅתְֹגׁأ • Native Container • 5.6ַ♧鿇㹋鄲幥 ד2018.1βד㹋鄲幥 • C# JobSystem • 2018.1 βד㹋鄲幥 • Entity Conponent System(ECS) • 2018.1.0דծExperimental(㹋꿀涸堣腉)ֲⰅג׃ה • Burst Compiler • 2018.1.0דծExperimental(㹋꿀涸堣腉)ֲⰅג׃ה
넝鸞⻉➙ךך䖓ך堣腉גְאח • Unityד٦ي⡲דְֲִֻגծءوٝך䚍腉剑㣐ꣲ涪䳸ׅ חծ㣐ֹֻ4ךא堣腉ָ➙䖓ٔٔ٦ׅתְֹגׁأ • Native Container • 5.6ַ♧鿇㹋鄲幥 ד2018.1βד㹋鄲幥 • C# JobSystem • 2018.1 βד㹋鄲幥 • Entity Conponent System(ECS) • 2018.1βתכח劢㹋鄲կ植㖈 ؙٗ٦דسؤꂁ䋒⚥կ 私からの話はコチラになります。 • Burst Compiler • 2018.1βתכח劢㹋鄲կ植㖈 ؙٗ٦דسؤꂁ䋒⚥կ
ֿךדתUnity㉏ך겗挿 • GCָꅾְ • C#欽ח然⥂ׁכًٌٔծ،ٔف穄✪דת鵤ְׁז • 僇爙涸חThread甧הְזגծC#ָגץׅרקךزفؙٔأMainThread ♳ד㹋遤ׁ
NativeContainerד箢ㄤׁ㉏겗挿 • GCָꅾְ • 孡鯪 חnewָ⳿勻ְז • C#欽ח然⥂ׁכًٌٔծ،ٔف穄✪דת鵤ְׁז • ♧儗涸ؿحغז؋٦然⥂הׅծ⟃䖓ًٌٔך㖇鶕ׅ
㉏겗挿ךGC ًٌٔ$חծ倜ד♴ְ朐屣ז$ָًٌٔ駈 ًٌٔ$גׇ饥儗ծ($׃הֲ״׃⥂然 կׅת׃הֲ״ֽ瑞 في٦ծְꅾח($Ⳣ椚ָꬊ䌢ךָֿծ ׅת׃ָ涪欰ؗخؙؕח⚥؎ٖ
ManagedMemory㉏ך겗挿 $欽♧ג׃ה䏝然⥂ׁחيذأءכًٌٔ鵤ׁ׆ծ ،ٔف穄✪⥂דת䭯ׁׅתְת׃גկ C#使用メモリ 未使用 C#用に確保したメモリ Unityメモリ アプリで使用している全体メモリ
NativeContainerגְאח ։GC כה搀ꟼ⤘זMemory然⥂։
NativeContainerגְאח • GC㼎韋ְזזח䕎ًٌٔד然⥂ׅNativeArray؝ךוזׅٝדشذ • AllocateהDispose(C++ךדnewהdeleteךꟼ⤘)荈魦ד盖椚ֽז׃ ׇתְֽל • 䖓וק稱➜ׅJobSystem/ECS瘝ַ♴佄ִꬊ䌢חꅾ銲ז堣腉ׅד • unsafeזC#ד剅ְַגծ؝ٝشذ倜鋉ח㹀纏הֿׅ〳腉 ׅד
NativeContainer ך⢪欽⢽
// Vector3ךꂁNativeArrayג׃ה然⥂ׅת׃
NativeArray<Vector3> array = new NativeArray<Vector3>(size, Allocator.Persistent);
// 兛鸐ךꂁחֲ״ך⢪欽〳腉ׅד
for( int i = 0; i < array.Length; ++i ){
array[ i ] = array[i] + Vector3.one;
}
// 然⥂ًٌٔ׃鍑佝ׅת׃կ
array.Dispose();
NativeContainer ך⢪欽⢽
// Vector3ךꂁNativeArrayג׃ה然⥂ׅת׃
NativeArray<Vector3> array = new NativeArray<Vector3>(size, Allocator.Persistent);
// 兛鸐ךꂁחֲ״ך⢪欽〳腉ׅד
int や float 等のプリミティブなタイプか、
for( int i = 0; i < array.Length; ++i ){
Vector3等のstruct型のみが指定可能です。
array[ i ] = array[i] + Vector3.one;
}
class型は指定不可です
// 然⥂ًٌٔ׃鍑佝ׅת׃կ
array.Dispose();
NativeContainer ך⢪欽⢽
// Vector3ךꂁNativeArrayג׃ה然⥂ׅת׃
NativeArray<Vector3> array = new NativeArray<Vector3>(size, Allocator.Persistent);
// 兛鸐ךꂁחֲ״ך⢪欽〳腉ׅד
メモリの生存期間に合わせて、以下の三つの中から選択可能です。
for( int i = 0; i < array.Length; ++i ){
Allocator.Temp ( そのフレームのみ有効 )
array[ i ] = array[i] + Vector3.one;
}
Allocator.TempForJob( そのJob中のみ有効)
Allocator.Persistent( 解放するまで有効 )
// 然⥂ًٌٔ׃鍑佝ׅת׃կ
array.Dispose();
下に行くほど、メモリ確保のコストが高くなります。
NativeContainerך珏겲גְאח • 2018.1.0b儗挿ךְגⰅד • NativeArray • NativeSlice ( NativeArrayַ♧鿇ⴖ《瘝ח⢪欽) • ➙䖓鷄⸇✮㹀 • NativeList • NativeHashMap • NativeMultiHashMap • NativeQueue ※ NativeContainer 自体は、unsafeなC#で書かれているため、 コンテナを新規に定義することも可能
NativeContainerד鍑佝䘌 Memory Leakכדךׅ
כדךׅMemoryLeak ׅת׃涪欰כؙծًٌٔٔ٦ה䘌ןㄎ• Dispose ָؙծٔ٦גְגזחֲ״ׅ湊鋔ؙٔ٦כדծEditor橆㞮׃ • ׅת⳿ָآ٦إحًؒٓ٦הׅ涪欰 ׇתְ遤כ湊鋔ؙծٔ٦ך鿪さך瘝أٝوؓ٦ؿػכד♳• 㹋堣
C# JobSystemד箢ㄤׁ㉏겗 • 僇爙涸חThread甧הְזגծC#ָגⰋרקךزفؙٔأMainThread♳ ד㹋遤ׁ • Unityָ甧גWorkerThreadⰻؒכٝآٝⰻ鿇Ⳣך椚㼔欽גְגזהծ C#Ⳣךزفؙٔأ椚㹋遤⳿ָהֿׅ勻׃דׇת
植㖈ךThread崞欽朐屣 (1) C#スクリプトの処理は基本的に MainThread上で実行されます
植㖈ךThread崞欽朐屣(2) UnityではMainThreadだけが働いていて、 Worker ThreadはIdle状態になってしまい 何も仕事をしていない状態がよくあります
C# JobSystemגְאח ։⡭ְגWorkerThread⢪ְⴖך堣腉։
C# JobSystemגְאח • ؒٝآٝⰻ鿇Ⳣ椚㼔欽ךWorkerThreadC#⩎Ⱅח׃ծC#زفؙٔأ Ⳣ椚WorkerThreadⴓח䬐ָׁהׇֿ〳腉ׅת׃ח • C#כד⩎زفؙٔأ㹋遤ׁⰻְׇ㺁Jobג׃ה涫ꐮ׃ծ WorkerThreadⳢח椚ⴓ䬐ָׁהׇֿ〳腉ׅד • ׃MainThread ⟃㢩ַ㹋遤ׁדךזח✲ծךוהק UnityAPIㄎ⳿ָ✲ׅ⳿ן勻זֻז
C# JobSystemⵃך欽䖓גְאח Idle状態で遊んでいたWorker ThreadにもC#スクリプト処理を 分担させることで全体の負荷を下げる事につながります
C# JobSystemדぢֻ✲ծぢַ✲ְז • 1ٖؿ٦חⰻ⟃ي ת稢ְַ⡲噟䫎ך־䲿ח⡲ׅתְג • ぢֻ✲ • 醱侧زؙؑآـؔך刿倜Ⳣ椚⚛遤Ⳣג׃椚ׅ • ぢַ✲ְז • ؟٦غ٦ךה鸐⥋Ⳣ椚ꬊず劍Ⳣד椚ׅ → 䖞勻鸐 C#⩎דThread甧ג瘝ך䩛岀ָ葺ְׅדկ
C# JobSystemⵖךד秈 • C# Heapךפ،⳿ָأإؙ勻ְז • NativeContainerכֻ׃ծָؤ؎؟㔿㹀ׁstructַ׃،⳿ָأإؙ 勻ׇת • 婁ךוUnity APIךפ،⳿ָأإؙ勻ְז • Mathٓ؎װٔٓـծJobSystemחך欽䠐ׁAPIⵃַ׃欽ָ⳿勻 ׇת
Jobך⡲䧭גְאח • 㣐ֹֻ3鸐 ךJobUnityכד欽䠐ׅתְג׃ • ⡦ַ㣐ֹⳢז椚 ךׅIJob • ꂁךぐ銲稆⽃⡘⚛ד遤Ⳣג׃椚遤ֲ ךIJobParalellFor • 醱侧ךTransformח㼎⚛ג׃遤Ⳣג׃椚遤ֲך IJobParallelForTransform
⡦ַ㣐ֹⳢז椚 ךׅIJob(1) // Jobの定義部分 struct MyJob : IJob{ // Jobに渡したいパラメーター public int param; // 実際に実行する処理 public void Execute( ){ int sum = 0; for( int i = 0 ; i < param ; ++ i){ sum += i ; } Debug.Log( “sum “ + sum ); } } // MonoBehaviourのUpdate内等で実際にJobを発行するところ // Jobを作成します var job = new MyJob(){ param = 100 }; // Jobを発行してます JobHandle handle = job.Schedule(); // 発行したJobが完了するのを待ちます handle.Complete();
⡦ַ㣐ֹⳢז椚 ךׅIJob(1) // Jobの定義部分 Jobを定義するには、structを作成します。 struct MyJob : IJob{ // Jobに渡したいパラメーター Job実行に必要なデータをメンバーに持たせます。 public int param; // 実際に実行する処理 メンバーは int/float等のプリミティブな型もしくはstruct/ public void Execute( ){ int sum = 0; NativeArrayしか持てません。UnityEngine.Objectのような参照型のメ for( int i = 0 ; i < param ; ++ i){ sum += i ; } Debug.Log( “sum “ + sum ); ンバーは持つことが出来ません } } 実行そのものは Executeメソッドに記述します // MonoBehaviourのUpdate内等で実際にJobを発行するところ // Jobを作成します var job = new MyJob(){ param = 100 }; // Jobを発行してます JobHandle handle = job.Schedule(); // 発行したJobが完了するのを待ちます handle.Complete();
⡦ַ㣐ֹⳢז椚 ךׅIJob(2) // Jobの定義部分 struct MyJob : IJob{ // Jobに渡したいパラメーター public int param; // 実際に実行する処理 public void Execute( ){ int sum = 0; for( int i = 0 ; i < param ; ++ i){ sum += i ; } Debug.Log( “sum “ + sum ); } } // MonoBehaviourのUpdate内等で実際にJobを発行するところ // Jobを作成します var job = new MyJob(){ structをnewして、Schedule()メソッドを呼び出すこと param = 100 で、Jobの発行が出来ます。 }; // Jobを発行してます Scheduleからの返り値のJobHandleオブジェクトで発 JobHandle handle = job.Schedule(); // 発行したJobが完了するのを待ちます 行したJobを待たせることが出来ます handle.Complete();
ꂁךぐ銲稆⽃⡘⚛ד遤Ⳣג׃椚遤ֲך IJobParalellFor NativeArray< Vector3 > position [0] [1] [2] [3] [4] ... ( 0, 0, 10 ) ( 0, 0, 20 ) ( 0, 0, 30 ) ( 0, 0, 40 ) ( 0, 0, 50 ) ... これに対して、( 0,0,1 )を足していく ( 0, 0, 11) ( 0, 0, 21 ) ( 0, 0, 31 ) ( 0, 0, 41 ) IJobParallelForを使えば、この 「(0,0,1) を足す作業」を 平行して処理させることが出来ます。 ( 0, 0, 51 )
ⴓ侔䖓Ⳣך椚؎ً٦آ 分散前 MainThread position [ 0~30 ]の更新処理 分散後 MainThread Jobの発行及び完了待ち position [ 0~10 ]の更新処理 Worker position [ 11~20 ]の更新処理 Worker position [ 21~30 ]の更新処理
ꂁךぐ銲稆⽃⡘⚛ד遤Ⳣג׃椚遤ֲך
IJobParalellFor
// Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
public float deltaTime;
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
public void Execute( int index ){
positions[ index ] = positions[ index ] + Vector3.front * deltaTime;
}
}
// MonoBehaviourのUpdate内等で実際にJobを発行するところ
// Jobを作成します
var job = new MyPositionUpdate(){
positions = bulletPositions, // <- NativeArray<Vector3> bulletPositions;が定義されているつもり
deltaTime = Time.deltaTime ,//<- Main以外からはTime.deltaTimeが呼べないので…
};
// Jobを発行してます。対象の配列要素数・バッチ数を指定(0で良い感じに設定してくれる)
JobHandle handle = job.Schedule( positions.Length ,0 );
// 発行したJobが完了するのを待ちます
handle.Complete();
ꂁךぐ銲稆⽃⡘⚛ד遤Ⳣג׃椚遤ֲך
IJobParalellFor
// Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
public float deltaTime;
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
public void Execute( int index ){
更新の対象は、Nativeコンテナである必要があります。
positions[
index ] = positions[ index ] + Vector3.up * deltaTime;
}
}
// MonoBehaviourのUpdate内等で実際にJobを発行するところ
// Jobを作成します
var job = new MyPositionUpdate(){
positions = bulletPositions, // <- NativeArray<Vector3> bulletPositions;が定義されているつもり
deltaTime = Time.deltaTime ,//<- Main以外からはTime.deltaTimeが呼べないので…
};
// Jobを発行してます。対象の配列要素数・バッチ数を指定(0で良い感じに設定してくれる)
JobHandle handle = job.Schedule( positions.Length ,0 );
// 発行したJobが完了するのを待ちます
handle.Complete();
ꂁךぐ銲稆⽃⡘⚛ד遤Ⳣג׃椚遤ֲך
IJobParalellFor
// Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
public float deltaTime;
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
public void Execute( int index ){
positions[ index ] = positions[ index ] + Vector3.up * deltaTime;
}
}
予期せぬエラーを防ぐため、引数で渡された「index」以外に
// MonoBehaviourのUpdate内等で実際にJobを発行するところ
//書き込もうとすると、Editor上ではエラーメッセージが出るよ
Jobを作成します
var
job = new MyPositionUpdate(){
うになっています。
positions = bulletPositions, // <- NativeArray<Vector3> bulletPositions;が定義されているつもり
deltaTime = Time.deltaTime ,//<- Main以外からはTime.deltaTimeが呼べないので…
};
// Jobを発行してます。対象の配列要素数・バッチ数を指定(0で良い感じに設定してくれる)
JobHandle handle = job.Schedule( positions.Length ,0 );
// 発行したJobが完了するのを待ちます
handle.Complete();
複数のTransformに対して並行して処理を行うための IJobParallelForTransform IJobParallelForTransformを利用することで、このCubeの移 動や回転をWorkerThreadでもできるようになります。 (通常だと transformへの書き込みはMainThread以外からでは できませんが、 IJobParallelForTransformの TransformAccess経由ならば可能です)
複数のTransformに対して並行して処理を行うための
IJobParallelForTransform
// Jobの定義部分
struct MyTransformUpdateJob : IJobParallelForTransform{
// Jobに渡したいパラメーター
public int objNum;
public float time;
// 実際に実行する処理 indexには配列の何個目であるか、transform に対して値をセットします
public void Execute(int index, TransformAccess transform){
transform.position = new Vector3( index – objNum / 2 , time , 0.0f);
}
}
// MonoBehaviourのUpdate内等で実際にJobを発行するところ
Transform[] transformArray = "xxx";
// 並行して処理をしたいTransformのリストを定義します
var transformAccessArray = new TransformAccessArray(transformArray);
// Jobを作成します
var myTransformUpdateJob = new MyTransformUpdateJob(){
time = Time.deltaTime , objNum = transformArray.Length
};
// Jobを発行して、直後で完了待ち
JobHandle handle = myTransformUpdateJob.Schedule( transformAccessArray );
handle.Complete();
複数のTransformに対して並行して処理を行うための
IJobParallelForTransform
// Jobの定義部分
struct MyTransformUpdateJob : IJobParallelForTransform{
// Jobに渡したいパラメーター
public int objNum;
public float time;
// 実際に実行する処理 indexには配列の何個目であるか、transform に対して値をセットします
public void Execute(int index, TransformAccess transform){
transform.position = new Vector3( index – objNum / 2 , time , 0.0f);
}
引数で渡ってくるTransformAccess経由でpositionやrotationを
}
設定します。
// MonoBehaviourのUpdate内等で実際にJobを発行するところ
Transform[] transformArray = "xxx";
// 並行して処理をしたいTransformのリストを定義します
var transformAccessArray = new TransformAccessArray(transformArray);
// Jobを作成します
var myTransformUpdateJob = new MyTransformUpdateJob(){
time = Time.deltaTime , objNum = transformArray.Length
};
// Jobを発行して、直後で完了待ち
JobHandle handle = myTransformUpdateJob.Schedule( transformAccessArray );
handle.Complete();
Jobⵃيذأء欽׃㼔欽API • 植㖈JobSystemⵃ欽׃API ♴כ鎸 • 醱侧ךRaycastJobⵃ欽⚛ג׃遤ג׃㹋遤ׅRayCastCommand • NaviMeshٕך٦ز嗚稊Job⚛ג׃ח遤ג׃㹋遤ׅNavMeshQuery
C JobSystem剣⸬崞欽הׅծ ٔذحغ٦嶊顤䫇ִׅת
㹋꿀ג׃穠卓 https://github.com/wotakuro/UnityJobSystemTest こちらで計測。GPU・通信の影響を抑えるため、機内モード/画面輝度最低固定、ゲームのFPSを10固定 にし、CPUでのバッテリー消費が顕著に出る環境にして実験。 Android / iOS共に、JobSystemにして処理時間が減った結果結果、CPUの休む時間が増えたり、MainThreadへの負荷が減るので CPUの実行周波数を下げることが出来た等で、バッテリー消費が下がったと思われる。
C# JobSystem ⵖך䖴גְאח
C# JobSystemⵖך䖴גְאח QJobחPriority(⮚⯓䏝)⳿כֽב勻ַׅת APriorityָׇת֮כծJobךד⣛㶷ꟼ⤘כ鏣㹀ׅתֹד Job BכծJob Aח⣛㶷הׅ鏣㹀׃㜥さծJob Bָ㸣✪דתׅJob Aכ 㹋遤ׇׁת
WorkerThreadך侧גְאח
WorkerThreadך侧גְאח WorkerThreadの数はプラットフォーム・マシンの ハードウェアによって変わってきます。 多くのプラットフォームでは、コア数 – 1 だけ WorkerThreadに割り当てされます
Job㹋遤儗ٓؒך٦גְאח
Job㹋遤儗ٓؒך٦גְאח Job.Schedule時に、InvalidOperationException等が出てしまうことがあります。 これはJobの実行タイミングで結果が異なりうる時にエラーとなってしまう可 能性があるのをUnityEditorが察知してエラーを出しています。 異なる二つのJobが同じ参照先のデータを有していた場合等に発生します。
Job㹋遤儗ٓؒך٦גְאח Worker Worker Job A positions Job B positions NativeArray< Vector3 > positions [0] [1] [2] [3] [4] ... ( 0, 0, 10 ) ( 0, 0, 20 ) ( 0, 0, 30 ) ( 0, 0, 40 ) ( 0, 0, 50 ) ...
Job㹋遤儗ٓؒך٦גְאח Worker Job A positions 同時に走っているJobが同じ参照先( NativeContainer )を指 していた場合に複数のJobから同一のリソース書き込みを Worker Job B される可能性を考慮してEditor実行時にはエラーを出すよ positions うにしています。 NativeArray< Vector3 > positions [0] [1] [2] [3] [4] ... ( 0, 0, 10 ) ( 0, 0, 20 ) ( 0, 0, 30 ) ( 0, 0, 40 ) ( 0, 0, 50 ) ...
Job㹋遤儗ٓؒך٦גְאח
Worker
Job A
positions
書き込みを行わないのであれば、Jobの変数宣言に[ReadOnly] 属性をつけて、
このpositionsへの書き込みは行わない事を明示的に教えます。
Worker
struct MyJob : IJob{
Job B
positions
[ReadOnly]
public NativeArray<Vector3> positions;
}
また[ReadOnly]の反対で、[WriteOnly]属性もあります。
NativeArray< Vector3 > positions
[0]
[1]
[2]
[3]
[4]
...
( 0, 0, 10 )
( 0, 0, 20 )
( 0, 0, 30 )
( 0, 0, 40 )
( 0, 0, 50 )
...
IJobParallelFor 㹋遤儗ٓؒך٦גְאח Job.Schedule実行中に IndexOutOfRangeExceptionが出てきました。 しかし配列外のアクセスには心当たりがありません…。 とりあえず、ソースコードを見てみましょう…
IJobParallelFor 㹋遤儗ٓؒך٦גְאח
// Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
// 敵のタイプ別の速度
public NativeArray<Vector3> velocityByEnemyType;
// 敵のタイプ指定
public NativeArray<int> enemyType;
// deltaTIme
public float deltaTime;
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
public void Execute( int index ){
int enemyType = enemyType[index];
Vector3 velocity = velocityByEnemyType[ enemyType ];
positions[ index ] = positions[ index ] + Vector3.front * deltaTime;
}
}
IJobParallelFor 㹋遤儗ٓؒך٦גְאח
// Jobの定義部分
ここで実行時にエラーが出ます。
struct MyPositionUpdate : IJobParallelFor{
これは velocityByEnemyType[ enemyType ]と、引数「index」以外にアクセスし
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
ているためです。
// 敵のタイプ別の速度
public NativeArray<Vector3> velocityByEnemyType;
アクセスがReadなのかWriteなのかまでは判別は判別できませんが、指定され
// 敵のタイプ指定
たindex以外にアクセスが起きている事だけは UnityEditor側で検知が出来ます。
public NativeArray<int> enemyType;
// deltaTIme
なので、実行時にエラーを出します
public float deltaTime;
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
public void Execute( int index ){
int enemyType = enemyType[index];
Vector3 velocity = velocityByEnemyType[ enemyType ];
positions[ index ] = positions[ index ] + Vector3.front * deltaTime;
}
}
IJobParallelFor 㹋遤儗ٓؒך٦גְאח
// Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
// 敵のタイプ別の速度
[ReadOnly]
public NativeArray<Vector3> velocityByEnemyType;
// 敵のタイプ指定
public NativeArray<int> enemyType;
velocityByEnemyTypeに対して [ReadOnly]属性をつけてあげる事で、
// deltaTIme
public float deltaTime;
velocityByEnemyTypeへの書き込みを行わないことを明示する事で実行時エラーが
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
回避できます。
public void Execute( int index ){
int enemyType = enemyType[index];
Vector3 velocity = velocityByEnemyType[ enemyType ];
positions[ index ] = positions[ index ] + Vector3.front * deltaTime;
}
}
C# JobSystemؙحصؙذךד
如Update⯓ך걧ד㸣✪䖉״דהֿׅ넝鸞⻉ׅת׃ class JobTest : MonoBehaviour{ private JobHandle handle; public Transform[] transformArray; void Update(){ //前のフレームで発行したJobが完了するのを待ちます jobHandle.Complete(); // 並行して処理をしたいTransformのリストを定義します var transformAccessArray = new TransformAccessArray(transformArray); // Jobを作成します var myTransformUpdateJob = new MyTransformUpdateJob(){ time = Time.deltaTime , objNum = transformArray.Length }; // Jobを発行してます。この時点では、Todo用のQueueに積まれるだけです JobHandle handle = myTransformUpdateJob.Schedule( transformAccessArray ); // [重要] 実際に積まれたJobの実行を促します JobHandle.ScheduleBatchedJobs(); } }
如Update⯓ך걧ד㸣✪䖉״דהֿׅ넝鸞⻉ׅת׃ MainThread Jobの発行及び完了待ち エンジン側での描画等の更新処理 EngineのJob Job更新処理 Worker Jobの更新処理 EngineのJob Worker Jobの更新処理 EngineのJob エンジンの処理の隙間にユーザーの処理を埋める 事で全体での処理時間を縮める事が可能です MainThread 完了 Job 待ち 発行 エンジン側での描画等の更新処理 EngineのJob EngineのJob Worker Jobの更新処理 EngineのJob Worker Jobの更新処理 Jobの更新処理
闌纏䕎䒭דת؝؝כ ֮כה㹋騧箟
劤傈ך怴统גְאח Work1.IJob Work3.IJobParallelForTransform Work2.IJobParallelFor Work4.RayCastCommand