godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
88.87k stars 20.15k forks source link

Unable to use ResourceLoader in C# after threaded load in GDScript #92798

Closed xill47 closed 1 day ago

xill47 commented 3 months ago

Tested versions

System information

Godot v4.3.beta1.mono - Windows 10.0.26120 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3080 Ti (NVIDIA; 31.0.15.5222) - AMD Ryzen 9 5900X 12-Core Processor (24 Threads)

Issue description

Originally reported here: https://discord.com/channels/212250894228652034/389794884585914368/1247671144983564370

After loading a resource with ResourceLoader.load_threaded_request. load_threaded_get_status and load_threaded_get in GDScript, ResourceLoader becomes unusable in .NET, failing with

ERROR: System.TypeInitializationException: The type initializer for 'Godot.ResourceLoader' threw an exception.
 ---> Godot.GodotObject+NativeMethodBindNotFoundException: Unable to find the native method bind. (Method 'ResourceLoader.load_threaded_get_status')
   at Godot.GodotObject.ClassDB_get_method_with_compatibility(StringName type, StringName method, UInt64 hash) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs:line 258
   at Godot.ResourceLoader..cctor() in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Generated/GodotObjects/ResourceLoader.cs:line 80
   --- End of inner exception stack trace ---
   at Godot.ResourceLoader.Load(String path, String typeHint, CacheMode cacheMode) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Generated/GodotObjects/ResourceLoader.cs:line 118
   at Godot.ResourceLoader.Load[T](String path, String typeHint, CacheMode cacheMode) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs:line 27
   at Godot.GD.Load[T](String path) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs:line 129
   at GodotDotnetThreadedLoad.SceneToLoad._Ready() in C:\Users\x-i-l\source\repos\_godot_bugs\godot-dotnet-threaded-load\SceneToLoad.cs:line 9
   at Godot.Node.InvokeGodotClassMethod(godot_string_name& method, NativeVariantPtrArgs args, godot_variant& ret) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Generated/GodotObjects/Node.cs:line 2401
   at GodotDotnetThreadedLoad.SceneToLoad.InvokeGodotClassMethod(godot_string_name& method, NativeVariantPtrArgs args, godot_variant& ret) in C:\Users\x-i-l\source\repos\_godot_bugs\godot-dotnet-threaded-load\.godot\mono\temp\obj\Debug\Godot.SourceGenerators\Godot.SourceGenerators.ScriptMethodsGenerator\GodotDotnetThreadedLoad.SceneToLoad_ScriptMethods.generated.cs:line 40
   at Godot.Bridge.CSharpInstanceBridge.Call(IntPtr godotObjectGCHandle, godot_string_name* method, godot_variant** args, Int32 argCount, godot_variant_call_error* refCallError, godot_variant* ret) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs:line 24
   at: void Godot.NativeInterop.ExceptionUtils.LogException(System.Exception) (/root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs:113)

Can be worked around by mentioning ResourceLoader beforehand (for example in an autoload)

Steps to reproduce

Minimal reproduction project (MRP)

https://github.com/xill47/godot-dotnet-threaded-load

Workaround: https://github.com/xill47/godot-dotnet-threaded-load/tree/workaround

raulsntos commented 3 months ago

It looks like ClassDB can't find the method with the provided hash.

When the ResourceLoader::load_threaded_get_status method is bound, the computed hash is 4137685479. This is the same hash that C# is using to retrieve the method. However, when C# tries to retrieve this method the computed hash is 1185939122, so it doesn't match.

Why is the computed hash different? The default value for the progress argument is an empty array ([]), but when C# tries to retrieve this method, the default value was a non-empty array ([1]).

https://github.com/godotengine/godot/blob/96a386f3c424af96d950ee5098b4b0e4907c9508/core/core_bind.cpp#L127

It looks like when the argument is not provided, the method modifies the default array:

https://github.com/godotengine/godot/blob/96a386f3c424af96d950ee5098b4b0e4907c9508/core/core_bind.cpp#L55-L61

In your workaround, since C# retrieves the method before it's called (and thus before the default array is modified), ClassDB is able to find the method and C# caches it.

Another way to workaround this would be to provide a value for the progress argument:

diff --git a/ThreadedLoadFromGDScript.gd b/ThreadedLoadFromGDScript.gd
index 8fc1c1c..094927c 100644
--- a/ThreadedLoadFromGDScript.gd
+++ b/ThreadedLoadFromGDScript.gd
@@ -17,7 +17,7 @@ func _process(delta):
        _threaded_process()

 func _threaded_process():
-   var load_status = ResourceLoader.load_threaded_get_status(scene_path)
+   var load_status = ResourceLoader.load_threaded_get_status(scene_path, [])
    if load_status == ResourceLoader.THREAD_LOAD_LOADED:
        var node = ResourceLoader.load_threaded_get(scene_path).instantiate()
        add_child(node)
AThousandShips commented 3 months ago

This is essentially a duplicate of:

brno32 commented 2 months ago

still an issue in v4.3.beta2.mono

Prakkkmak commented 1 month ago

Still in v4.3.beta3.mono