ジェネリックジョブ
Burst でのジョブのコンパイル方法は以下の 2 つです。
- エディターでは、ジョブのスケジュール時にコンパイルします。これは実行時 (JIT) コンパイルと呼ばれます。
- プレイヤービルドでは、ビルド対象のプレイヤーの一部としてジョブをコンパイルします。これは事前 (AOT) コンパイルと呼ばれます。
詳細については、コンパイル に関するドキュメントを参照してください。
ジョブが具象型 (ジェネリックを使用しない) の場合、Burst は両方のモード (AOT および JIT) でコンパイルします。ただし、ジェネリックジョブの動作が予期しないものになる可能性があります。
Burst はジェネリックをサポートしていますが、ジェネリックジョブや関数ポインターのサポートは限定的です。スケジュール済みのジョブがエディターではフルスピードで実行される一方で、ビルドしたプレイヤーではそうならない場合は、ジェネリックジョブに関連した問題が発生している可能性があります。
以下の例では、ジェネリックジョブを定義しています。
// 直接的なジェネリックジョブ
[BurstCompile]
struct MyGenericJob<TData> : IJob where TData : struct {
public void Execute() { ... }
}
また、ジェネリックジョブはネストすることもできます。
// ネストされたジェネリックジョブ
public class MyGenericSystem<TData> where TData : struct {
[BurstCompile]
struct MyGenericJob : IJob {
public void Execute() { ... }
}
public void Run()
{
var myJob = new MyGenericJob(); // 暗示的に MyGenericSystem<TData>.MyGenericJob を指す
myJob.Schedule();
}
}
Burst のコンパイル対象にならないジョブは、以下のようになります。
// 直接的なジェネリックジョブ
var myJob = new MyGenericJob<int>();
myJob.Schedule();
// ネストされたジェネリックジョブ
var myJobSystem = new MyGenericSystem<float>();
myJobSystem.Run();
どちらの場合もプレイヤービルドでは、Burst コンパイラーは MyGenericJob<int>
と MyGenericJob<float>
のコンパイルが必要であることを検出します。これは、ジェネリックジョブ (またはネストされたジョブのジェネリックジョブを囲む型) が、完全に解決済みのジェネリック引数 (int
と float
) とともに使用されているからです。
しかし、これらのジョブがジェネリックパラメーターを介して間接的に使用される場合、Burst コンパイラーではプレイヤーのビルド時にコンパイルする必要があるジョブを検出できません。
public static void GenericJobSchedule<TData>() where TData: struct {
// ジェネリック引数: ジェネリックパラメーター TData
// Burst コンパイラーはスタンドアロンプレイヤーのビルド時にこのジョブを検出できません。
var job = new MyGenericJob<TData>();
job.Schedule();
}
// 暗黙の MyGenericJob<int> は、エディターでは Burst のフルスピードで実行されますが、
// スタンドアロンプレイヤーのビルド時には検出されません。
GenericJobSchedule<int>();
型に由来するジェネリックパラメーターのコンテキストでジョブを宣言する場合も、同様の制限が適用されます。
// ジェネリックパラメーター TData
public class SuperJobSystem<TData>
{
// ジェネリック引数: ジェネリックパラメーター TData
// Burst コンパイラーはスタンドアロンプレイヤーのビルド時にこのジョブを検出できません。
public MyGenericJob<TData> MyJob;
}
ジェネリックジョブを使用する場合、完全に解決済みのジェネリック引数 (例: int
、MyOtherStruct
) を指定して直接的に使用する必要があります。ジェネリックパラメーターを介して使用することはできません (例: MyGenericJob<TContext>
)。
Important
Burst は、ジェネリックメソッドを介したジェネリックジョブのスケジューリングをサポートしていません。これはエディターでは動作しますが、スタンドアロンプレイヤーでは動作しません。
関数ポインター
関数ポインターには制限があります。Burst では関数ポインターを介してジェネリックデリゲートを使用できないためです。
public delegate void MyGenericDelegate<T>(ref TData data) where TData: struct;
var myGenericDelegate = new MyGenericDelegate<int>(MyIntDelegateImpl);
// この関数ポインターのコンパイルは失敗します。
var myGenericFunctionPointer = BurstCompiler.CompileFunctionPointer<MyGenericDelegate<int>>(myGenericDelegate);
この制限は、このようなデリゲートと相互運用する際の .NET ランタイムの制限に起因するものです。
詳細については、関数ポインター に関するドキュメントを参照してください。