Offload reading glTF_VRM_Meta properties to other task
glTF_VRM_Meta の持つプロパティには、長い時間がかかるものがあります。
このPRは IAwaitCaller.Run() を利用し、それを緩和するものです。
疑問点
カジュアルに IAwaitCaller.Run() を使用していますが、これが許されるものなのか、良く分かっていません。
手元の UnityEditor およびアプリケーションのターゲットでは問題無く動作していますが、WebGL等で困るケースがあり得るのか、事情を理解できていない部分があります。
Can one of the admins verify this patch?
PR ありがとうございます。
IAwaitCaller.Run は、 Task.Run を使うのが目的です。(RuntimeOnlyAwaitCaller )
- Editor では Task.Run の結果が待てない (UnitTest とか Asset ローダーが終わらなくなる)
- WebGL ではそもそもスレッド無い( NextFrame だけ実装できる )
という状況から逃げるために、 ImmediateCaller の即時実行に逃げられるようになっています。
https://github.com/vrm-c/UniVRM/blob/master/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/ImmediateCaller.cs#L19
Unity での Task.Run (スレッド実行) の条件は、Unity.Object にアクセスしない であると心得ております。
さもないと、 Main thread じゃないアクセス違反になってしまいます。
var meta = ScriptableObject.CreateInstance<VRMMetaObject>();
なので、 スレッド上で ScriptableObject にアクセスすることになって例外になりそうな気がします。
はい、この問題があると認識しています。
それゆえに IAwaitCaller を導入しています。
たとえば LoadAsync 内部で await するために Task を好き勝手に作ると WebGL で問題になる可能性が高いです。
そこで Task は IAwaitCaller を経由してのみ作成することとしています。
これによって WebGL などの環境では IAwaitCaller の実装を切り替えることにより、問題を回避することができます。
WebGL等で困るケースがあり得るのか
レビューありがとうございます。以下のような変更案を考えました。
案(1)
IAwaitCaller.Run() は使用したまま、一次変数を経由して、アクセス違反の可能性を防ぐ
string version = default;
AllowedUser allowedUser = default;
LicenseType licenseType = default;
...
await awaitCaller.Run(() =>
{
version = gltfMeta.version;
allowedUser = gltfMeta.allowedUser;
licenseType = gltfMeta.licenseType;
...
});
meta.Version = version;
meta.AllowedUser = allowedUser;
meta.LicenseType = licenseType;
...
長所
-
glTF_VRM_Metaの実装が変更されて処理速度が変わった場合、自動的にオフローディングできる。
短所
- 同じ名前が繰り返され、実装が嵩張っている
- いつも
IAwaitCaller.Run()が別コンテキストで実行される可能性があることを意識する必要がある
案(2)
元の形式に戻し、時間のかかる (or かかりそうな) 処理の後ろに IAwaitCaller.NextFrameIfTimedOut() を適宜追加する。
meta.AllowedUser = gltfMeta.allowedUser;
awaitCaller.NextFrameIfTimedOut();
...
meta.LicenseType = gltfMeta.licenseType;
awaitCaller.NextFrameIfTimedOut();
長所
- 従来通りの挙動を保てる
- 差分が少ない
短所
-
glTF_VRM_Metaの実装、処理時間を監視し、処理が重くなったときは適宜IAwaitCaller.NextFrameIfTimedOut()を追加する必要がある
https://github.com/vrm-c/UniVRM/pull/1823#discussion_r987641275 に従い、本件はクローズします。