Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PreviewSystem] IRenderFilter.Instantiate などがランダムでPlayerLoopの内外で呼び出される #409

Closed
anatawa12 opened this issue Sep 21, 2024 · 5 comments · Fixed by #410

Comments

@anatawa12
Copy link
Contributor

anatawa12 commented Sep 21, 2024

e70e43b で確認しました。

IRenderFilter.Instantiate などがランダムでPlayerLoopの内外で呼び出されてしまいます。

PlayerLoopの中で呼び出されるとRead/Write offなメッシュのアクセスがエラーになってしまうので、これが発生するか否かがランダムだとデバッグ時に微妙に困ります。

PlayerLoopのなかかどうかは_Z18IsInsidePlayerLoopvを呼び出せば確認できますがDllImportでは呼び出せないため様々なハックが必要となってしまいます。(正攻法は不明)

一応EditorApplication.updateなどは外であることが確定しています。

unity 2022.3.22f1 arm64でのみ動く確認用コードはありますが他環境だと確実に壊れますため汎用性がないです。
https://gist.github.com/anatawa12/f730f08940ef965cadcf25388c0afb1c

@bdunderscore
Copy link
Owner

こちらの環境では再現できてません(ProxyPipelineなどの中にMeshを参照するテストコードを追加などしてみてもだめっぽい)

Mac特有の事情の可能性もあるので、とりあえず発生時点でのスタックトレースなどをお願いします。

@anatawa12
Copy link
Contributor Author

スタックトレース的には変化がありません

少し今試した感じ、エディタをいじってるだけでは発生確率が低いですが、シーンウィンドウの視点移動をしていたりすると(PlayerLoopをトリガさせていると?)よく発生するように思えます

Instantiate IsPlayerLoop: True
UnityEngine.Debug:Log (object)
Anatawa12.AvatarOptimizer.EditModePreview.AAORenderFilterBase`1/<Instantiate>d__5<Anatawa12.AvatarOptimizer.RemoveMeshInBox>:MoveNext () (at ./Packages/AvatarOptimizer/Editor/EditModePreview/AAORenderFilterBase.cs:64)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<nadena.dev.ndmf.preview.IRenderFilterNode>:Start<Anatawa12.AvatarOptimizer.EditModePreview.AAORenderFilterBase`1/<Instantiate>d__5<Anatawa12.AvatarOptimizer.RemoveMeshInBox>> (Anatawa12.AvatarOptimizer.EditModePreview.AAORenderFilterBase`1/<Instantiate>d__5<Anatawa12.AvatarOptimizer.RemoveMeshInBox>&)
Anatawa12.AvatarOptimizer.EditModePreview.AAORenderFilterBase`1<Anatawa12.AvatarOptimizer.RemoveMeshInBox>:Instantiate (nadena.dev.ndmf.preview.RenderGroup,System.Collections.Generic.IEnumerable`1<System.ValueTuple`2<UnityEngine.Renderer, UnityEngine.Renderer>>,nadena.dev.ndmf.preview.ComputeContext)
nadena.dev.ndmf.preview.NodeController/<Create>d__23:MoveNext () (at ./Packages/ndmf/Editor/PreviewSystem/Rendering/NodeController.cs:121)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<nadena.dev.ndmf.preview.NodeController>:Start<nadena.dev.ndmf.preview.NodeController/<Create>d__23> (nadena.dev.ndmf.preview.NodeController/<Create>d__23&)
nadena.dev.ndmf.preview.NodeController:Create (nadena.dev.ndmf.preview.IRenderFilter,nadena.dev.ndmf.preview.RenderGroup,nadena.dev.ndmf.ObjectRegistry,System.Collections.Generic.List`1<System.ValueTuple`3<UnityEngine.Renderer, nadena.dev.ndmf.preview.ProxyObjectController, nadena.dev.ndmf.ObjectRegistry>>,string)
nadena.dev.ndmf.preview.NodeController/<Refresh>d__24:MoveNext () (at ./Packages/ndmf/Editor/PreviewSystem/Rendering/NodeController.cs:190)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<nadena.dev.ndmf.preview.NodeController>:Start<nadena.dev.ndmf.preview.NodeController/<Refresh>d__24> (nadena.dev.ndmf.preview.NodeController/<Refresh>d__24&)
nadena.dev.ndmf.preview.NodeController:Refresh (System.Collections.Generic.List`1<System.ValueTuple`3<UnityEngine.Renderer, nadena.dev.ndmf.preview.ProxyObjectController, nadena.dev.ndmf.ObjectRegistry>>,nadena.dev.ndmf.preview.RenderAspects,string)
nadena.dev.ndmf.preview.ProxyPipeline/<>c__DisplayClass22_2/<<Build>b__5>d:MoveNext () (at ./Packages/ndmf/Editor/PreviewSystem/Rendering/ProxyPipeline.cs:234)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<nadena.dev.ndmf.preview.NodeController>:Start<nadena.dev.ndmf.preview.ProxyPipeline/<>c__DisplayClass22_2/<<Build>b__5>d> (nadena.dev.ndmf.preview.ProxyPipeline/<>c__DisplayClass22_2/<<Build>b__5>d&)
nadena.dev.ndmf.preview.ProxyPipeline/<>c__DisplayClass22_2:<Build>b__5 (System.Threading.Tasks.Task`1<System.ValueTuple`3<UnityEngine.Renderer, nadena.dev.ndmf.preview.ProxyObjectController, nadena.dev.ndmf.ObjectRegistry>[]>)
UnityEngine.UnitySynchronizationContext:ExecuteTasks () (at /Users/bokken/build/output/unity/unity/Runtime/Export/Scripting/UnitySynchronizationContext.cs:107)
Instantiate IsPlayerLoop: False
UnityEngine.Debug:Log (object)
Anatawa12.AvatarOptimizer.EditModePreview.AAORenderFilterBase`1/<Instantiate>d__5<Anatawa12.AvatarOptimizer.RemoveMeshInBox>:MoveNext () (at ./Packages/AvatarOptimizer/Editor/EditModePreview/AAORenderFilterBase.cs:64)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<nadena.dev.ndmf.preview.IRenderFilterNode>:Start<Anatawa12.AvatarOptimizer.EditModePreview.AAORenderFilterBase`1/<Instantiate>d__5<Anatawa12.AvatarOptimizer.RemoveMeshInBox>> (Anatawa12.AvatarOptimizer.EditModePreview.AAORenderFilterBase`1/<Instantiate>d__5<Anatawa12.AvatarOptimizer.RemoveMeshInBox>&)
Anatawa12.AvatarOptimizer.EditModePreview.AAORenderFilterBase`1<Anatawa12.AvatarOptimizer.RemoveMeshInBox>:Instantiate (nadena.dev.ndmf.preview.RenderGroup,System.Collections.Generic.IEnumerable`1<System.ValueTuple`2<UnityEngine.Renderer, UnityEngine.Renderer>>,nadena.dev.ndmf.preview.ComputeContext)
nadena.dev.ndmf.preview.NodeController/<Create>d__23:MoveNext () (at ./Packages/ndmf/Editor/PreviewSystem/Rendering/NodeController.cs:121)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<nadena.dev.ndmf.preview.NodeController>:Start<nadena.dev.ndmf.preview.NodeController/<Create>d__23> (nadena.dev.ndmf.preview.NodeController/<Create>d__23&)
nadena.dev.ndmf.preview.NodeController:Create (nadena.dev.ndmf.preview.IRenderFilter,nadena.dev.ndmf.preview.RenderGroup,nadena.dev.ndmf.ObjectRegistry,System.Collections.Generic.List`1<System.ValueTuple`3<UnityEngine.Renderer, nadena.dev.ndmf.preview.ProxyObjectController, nadena.dev.ndmf.ObjectRegistry>>,string)
nadena.dev.ndmf.preview.NodeController/<Refresh>d__24:MoveNext () (at ./Packages/ndmf/Editor/PreviewSystem/Rendering/NodeController.cs:190)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<nadena.dev.ndmf.preview.NodeController>:Start<nadena.dev.ndmf.preview.NodeController/<Refresh>d__24> (nadena.dev.ndmf.preview.NodeController/<Refresh>d__24&)
nadena.dev.ndmf.preview.NodeController:Refresh (System.Collections.Generic.List`1<System.ValueTuple`3<UnityEngine.Renderer, nadena.dev.ndmf.preview.ProxyObjectController, nadena.dev.ndmf.ObjectRegistry>>,nadena.dev.ndmf.preview.RenderAspects,string)
nadena.dev.ndmf.preview.ProxyPipeline/<>c__DisplayClass22_2/<<Build>b__5>d:MoveNext () (at ./Packages/ndmf/Editor/PreviewSystem/Rendering/ProxyPipeline.cs:234)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<nadena.dev.ndmf.preview.NodeController>:Start<nadena.dev.ndmf.preview.ProxyPipeline/<>c__DisplayClass22_2/<<Build>b__5>d> (nadena.dev.ndmf.preview.ProxyPipeline/<>c__DisplayClass22_2/<<Build>b__5>d&)
nadena.dev.ndmf.preview.ProxyPipeline/<>c__DisplayClass22_2:<Build>b__5 (System.Threading.Tasks.Task`1<System.ValueTuple`3<UnityEngine.Renderer, nadena.dev.ndmf.preview.ProxyObjectController, nadena.dev.ndmf.ObjectRegistry>[]>)
UnityEngine.UnitySynchronizationContext:ExecuteTasks () (at /Users/bokken/build/output/unity/unity/Runtime/Export/Scripting/UnitySynchronizationContext.cs:107)

@anatawa12
Copy link
Contributor Author

anatawa12 commented Sep 22, 2024

今試したところ、windowsでも再現しました。

context.Observeでextractを指定していないIRenderFilterがある必要がありそうです

Windowsで確認するために使ったコードです。GUIDとfileIDをRead Write mesh offなメッシュにしてください。(この例ではあのんちゃんになってます)

static class IsInPlayerLoop
{
    private static Mesh? _mesh;
    private static System.Reflection.PropertyInfo? _propertyInfo;
    public static bool IsPlayerLoop() {
        if (_mesh == null)
        {
            // GUID and fileID of a read write mesh off mesh here
            var guid = "b90ae694f8009d949934a1419170452e";
            var fileID = 13727694889372365607;
            UnityEditor.GlobalObjectId.TryParse($"GlobalObjectId_V1-1-{guid}-{fileID}-0", out var globalObjectId);
            _mesh = (Mesh)UnityEditor.GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalObjectId);
        }
        if (_propertyInfo == null)
        {
            _propertyInfo = typeof(Mesh).GetProperty("canAccess", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!;
        }
        var canAccess = (bool)_propertyInfo.GetValue(_mesh)!;
        return !canAccess;
    }
}

@anatawa12
Copy link
Contributor Author

context.Observeでextractを指定していないIRenderFilterがある必要がありそうです

こちらはなくても、シーン上の動作を下にしたIRenderFilterの更新がある場合に発生することがありそうです(on macos with RemoveMesh in Boxのpreviewで確認)

@bdunderscore
Copy link
Owner

再現できました。どうやらUnityのデフォルトSynchronizationContextがPlayerLoop扱いになるため、TaskThrottleが起動するとPlayerLoopに移ってしまうようです。NDMF専用のSynchronizationContextで対応します。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants