問題
UnityのJob Systemを使ってMesh.verticesなどからいろいろな計算をしているのですが、JobのためにNativeArrayを作成する時にどうしても一定の間隔でGCスパイクが発生してしまい解決に困っています。
具体的な説明
SkinnedMeshRendererの頂点座標をvertices, bones, boneWeightsなどを使いワールド座標に変換するIJobParallelForのJobがあり、これを実行するコードは以下のようなものです。(最低限のもので、nullチェックなどは省略しています。)meshInfoはMeshのデータを格納したもので、例えばmeshInfo.verticesにはmeshInfoの初期化時にMesh.verticesを入れています。
このコードではアニメーションしているキャラクターの現在の頂点位置を求めるのに必要なboneTransformsを事前に計算しておき、そのあとさきほどのJobをスケジュールしています。そのあと、JobとJobHandleはリストにいれておき、LateUpdate()のタイミングでJobHandleのCompleteとNativeArrayのDisposeを行っています。(verticesとboneWeightsは[DeallocateOnJobCompletion]アトリビュートを使い自動で解放されるようにしています。)このコード自体はUpdateのタイミングで実行しています。
C#
1for (var boneId = 0; boneId < meshInfo.bones.Length; boneId++) 2{ 3 meshInfo.boneTransforms[boneId] = meshInfo.bones[boneId].localToWorldMatrix * meshInfo.bindposes[boneId]; 4} 5 6Profiler.BeginSample("job for meshInfo with SkinnedMeshRenderer"); // プロファイラに表示。 7var job = new GetLocalSkinnedMeshVertexPositionJob() 8{ 9 vertices = new NativeArray<Vector3>(meshInfo.vertices, Allocator.TempJob), 10 boneWeights = new NativeArray<BoneWeight>(meshInfo.boneWeights, Allocator.TempJob), 11 boneTransformMatricis = new NativeArray<Matrix4x4>(meshInfo.boneTransforms, Allocator.TempJob), 12 worldToLocalMatrix = w2lMat, 13 id = meshInfo.id, 14 result = new NativeArray<float3>(meshInfo.vertices.Length, Allocator.TempJob), 15}; 16 17var jobHandle = job.Schedule(meshInfo.vertices.Length, 1); 18Profiler.EndSample(); 19localSkinnedMeshVertexJobs.Add(job); // Jobをリストに追加。 20vertexJobHandles.Add(jobHandle); // JobHandleをリストに追加。
テストのためにUnityちゃんのSkinnedMeshRendererごとにMeshInfoを作成し毎フレーム上記の頂点座標変換のコードを実行してみたのですが、平均的には90fps近くほどでているなか、一定の間隔で大きなGCスパイクができてしまっています。これを解決したいです。
Deep Profileをみてみるとこのようになっていました。
プロファイルしたJob for meshInfo with SkinnedMeshRendererをみてみると、どうやらJob作成時のNativeArrayのコンストラクタが原因みたいです。毎回新しいNativeArrayを作っているのが悪いのかもしれないと思ったのですが、毎度作成しているのには理由があります。上記の変換Jobは本来毎フレームではないですがプレイヤーの入力に応じて行われ、さらにMesh.verticesのサイズが変わることもあるため、Allocater.Persistentで最初に作成して使いまわすということはしないほうがいいと判断し毎度Allocator.TempJobで作成するようにしていたのです。しかしそれが間違いで遅いといわれるAllocator.Persistentを頂点配列のサイズが変わるたびに作成し直すほうがいいのでしょうか。
また、もうひとつわからないことがあります。そもそもNativeArrayはアンマネージドでありGCが発生するのはおかしいと思うのですが、GCスパイクが発生してしまっているのはなぜなのでしょう。
具体的なIJobParallelForのJobはこの問題には関係ない(result以外はReadOnlyで、単純にウェイトの計算と座標変換してるだけ。)と思い書かなかったのですが、もし必要でしたらおっしゃってください。
よろしくお願いします。
環境
Unity 2019.1.1f1
Jobs 0.0.7
Burst 1.0.4
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/06/11 10:40