UniVRM icon indicating copy to clipboard operation
UniVRM copied to clipboard

Offload reading glTF_VRM_Meta properties to other task

Open matsutaka-pxv opened this issue 3 years ago • 4 comments

glTF_VRM_Meta の持つプロパティには、長い時間がかかるものがあります。 このPRは IAwaitCaller.Run() を利用し、それを緩和するものです。

疑問点

カジュアルに IAwaitCaller.Run() を使用していますが、これが許されるものなのか、良く分かっていません。 手元の UnityEditor およびアプリケーションのターゲットでは問題無く動作していますが、WebGL等で困るケースがあり得るのか、事情を理解できていない部分があります。

matsutaka-pxv avatar Sep 21 '22 11:09 matsutaka-pxv

Can one of the admins verify this patch?

vrm-github-bot avatar Sep 21 '22 11:09 vrm-github-bot

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 にアクセスすることになって例外になりそうな気がします。

ousttrue avatar Sep 27 '22 03:09 ousttrue

はい、この問題があると認識しています。 それゆえに IAwaitCaller を導入しています。 たとえば LoadAsync 内部で await するために Task を好き勝手に作ると WebGL で問題になる可能性が高いです。 そこで TaskIAwaitCaller を経由してのみ作成することとしています。 これによって WebGL などの環境では IAwaitCaller の実装を切り替えることにより、問題を回避することができます。

WebGL等で困るケースがあり得るのか

Santarh avatar Sep 27 '22 04:09 Santarh

レビューありがとうございます。以下のような変更案を考えました。

案(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() を追加する必要がある

matsutaka-pxv avatar Sep 28 '22 03:09 matsutaka-pxv

https://github.com/vrm-c/UniVRM/pull/1823#discussion_r987641275 に従い、本件はクローズします。

matsutaka-pxv avatar Oct 05 '22 08:10 matsutaka-pxv