Open Targma opened 11 months ago
Read: https://docs.godotengine.org/en/latest/tutorials/performance/thread_safe_apis.html#rendering. Using instantiate() in non-main thread is unrecommended. Use call_deferred(CallDeferred in C#) if you need it (will force them to be created in the main thread).
You should be getting an error like:
ERROR: This function in this node (Label) can only be accessed from either the main thread or a thread group.
Use call_deferred() instead.
Since you are using Task.Run
, you can easily replace it with Callable.From
and then CallDeferred
:
Callable.From(() =>
{
if (!SkipLabel)
{
var node = _packedScene.Instantiate<NumberLabel>();
node.Position = InitialPosition;
CallDeferred(Node.MethodName.AddChild, node);
}
var node2 = _packedScene2.Instantiate<SpriteNode>();
node2.Position = InitialPosition2;
CallDeferred(Node.MethodName.AddChild, node2);
}).CallDeferred();
You can also await the next process frame and that should continue in the main thread because of Godot's synchronization context:
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
Read: https://docs.godotengine.org/en/latest/tutorials/performance/thread_safe_apis.html#rendering. Using instantiate() in non-main thread is unrecommended. Use call_deferred(CallDeferred in C#) if you need it (will force them to be created in the main thread).
I am aware of that documentation suggestion, but with 4.2 came new multi-threading support and based on blog posts
https://godotengine.org/article/dev-snapshot-godot-4-1-dev-3/
We continue to improve Godot’s multi-threaded behavior and fix bugs from the recent changes. Such improvements include
multiple fixes to multi-threaded resource loading ([GH-74405](https://github.com/godotengine/godot/pull/74405),
[GH-77143](https://github.com/godotengine/godot/pull/77143)),
multiple fixes to the WorkerThreadPool class ([GH-76945](https://github.com/godotengine/godot/pull/76945), [GH-76999](https://github.com/godotengine/godot/pull/76999)), and
an early version of the multi-threaded node processing ([GH-75901](https://github.com/godotengine/godot/pull/75901)).
Thread safety of many engine and editor types has been improved, though this work is still ongoing and will continue into
Godot 4.2.
this is now officially a feature although in experimental stage As such i have working solution to just use GodotSynchronizationContext and dispatch action. But this takes time from main thread what we want to avoid.
Upon further investigation it appears instancing scenes with control nodes can cause crash.
instanciate()
call will crash or not (ex : AudioListener
& WorldEnvironment
never crash but AudioStreamPlayer
& NavigationAgent2D
always end up crashing)Sprite2D
)MeshInstance2D
)Sprite2D
crashes only if texture is set, MeshInstance2D
works even if texture is set)multithread-instanciate-crash.zip
It's GDScript only so anyone can open it. That said, GDScript is not my language. Sorry in advance if the code is not very clean
It consists of a simple Main.tscn
scene containing a single node with an attached instantiator.gd
script. The script simply instanciates the my_packed_scene.tscn
scene each frame until a crash occurs.
You can play with the following parameters :
NbThreads
: number of instantiation threadsInstancesPerFrame
: Number of scene instantiations per frameUseMutex
: encapsulate the Instantiate()
call with a mutexYou can add other nodes in my_packed_scene.tscn
to check if it crashes or not
my_packed_scene_crash_no_matter_what.tscn
is here only to keep tracks of the "Always crashing" nodes
(After 10000 successful frames, I considered it will never crash)
v4.3.dev6.mono.official [89850d553]
Godot v4.3.dev6.mono - Windows 10.0.19045 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 2080 (NVIDIA; 31.0.15.3623) - Intel(R) Core(TM) i7-4770K CPU @ 3.50GHz (8 Threads)
Godot version
4.2-beta1_mono_win64
System information
Windows 10 - Godot_v4.2-beta1_mono_win64 - Forward plus -dedicated
Issue description
C# Non-Main thread crash when calling instantiate
Node2D
withLabel (Control node)
Works normally when done with emptyNode2d
withoutLabel
andSprite2D
instead.Steps to reproduce
Calling
crashes on non-main thread. Call work first time but then crashes when called again
Full code
Main.cs
Number.cs
Minimal reproduction project
Link to project https://github.com/Targma/Godot-Crash-Replication
Modify to test different scenarios
Result when instantiated on main thread and crash otherwise