godotengine / godot

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

Node Instantiation Fails with NativeAOT Enabled in Export Release Mode #96072

Open atlasapplications opened 3 weeks ago

atlasapplications commented 3 weeks ago

Tested versions

System information

iOS 17.6.1 (Not in debug mode) Windows 11 (NativeAOT enabled & release mode)

Issue description

This is a bug that appears to only affect iOS based devices when distributing one's .ipa through Apple's App Store (so App Store Connect). This does not occur on iOS when distributing through a debug build on Xcode. When I first load my scene, many of the Nodes load properly, but when I get to the top level Node3D that contains most of my game level, it fails to instantiate. The error logs show this:

USER ERROR: Index nprops[j].value = 61545 is out of bounds (prop_count = 58). 
at: instantiate (scene/resources/packed_scene.cpp:314)

This leads me to believe that it could be related to Apple's restrictions on dynamic code via reflection which is still present in some of Godot's codebase for C#. I tested this with a release build on Android through Google Play and this error does not occur there either.

UPDATE It took a long time for me to figure out the exact configuration that was causing the primary world Node3D from instancing since my project has over a hundred Node3D instances that are a part of that PackedScene. However, I believe I've narrowed it down to a single MeshInstance3D that appears to be responsible.

This MeshInstance3D has a script attached that utilizes an [Export] variable of type StandardMaterial3D and applies that to the MeshInstance3D on _Ready(). I apply a default value in the editor. And in the actual root world where I drag and drop multiple instances I replace this [Export] value with various different StandardMaterial3D assets. When I remove the [Export] variable and just supply the StandardMaterial3D with ResourceLoader.Load() instead, the error no longer occurs.

Sooo, the workaround is to NOT use [Export] for Objects that inherit from [Resource]. As a side note, I also back all of my Resources that I instance from ResourceLoader into a static Dictionary that prevents premature disposal of Resources. This may or may not be a separate issue altogether which I note below, but seems related.

This is a difficult bug to reproduce as I've not found a way yet to create a minimal reproduction sample. My guess is that this bug only has a small chance of happening but since my project has so many eligible Nodes for this bug to occur, it was happening to me almost all the time. Nonetheless, here's how one may encounter it...

Steps to reproduce

Minimal reproduction project (MRP)

N/A

atlasapplications commented 1 week ago

Synopsis

It appears exporting with NativeAOT in release mode breaks C# projects in v4.3.

Breakdown

Further testing has revealed some important clues. Here's what I've tried since my last post:

  1. I now pool all references that inherit from Resource. This is because there may be a bug where custom C# resources are being disposed prematurely as demonstrated here: #83762
  2. I thought this may be the reason why there are a significant number of null references that prevent my top level Node3D scene from instancing.
  3. Although it may have fixed one issue, there seems to be another related one because I get a different error now:
USER ERROR: Scene instance is missing.
 at: instantiate (scene/resources/packed_scene.cpp:244)

This is the same method that fails above in packed_scene.cpp which is instantiate; however, the error gets further down.

A part of my testing was using NativeAOT on a different platform (in this case Windows) because that is the primary difference between publishing to App Store Connect, and say, Android) and this error did not occur initially. However, when I switched Export with debug off, the same exact error that occurs on iOS now happens on Windows. This may be a regression as I have tested NativeAOT on Windows with v4.2.2 and this error does not occur.

So it seems that when NativeAOT is used in combination with the release option, some Godot methods stop working when accessed from C#. My research shows this may be similar to: #94642

These are two commits that could have introduced the regression 465742d & be4cbee

atlasapplications commented 1 week ago

More testing has revealed a few important things.

Apparently, when I tested NativeAOT in the past, I didn't test it in release mode because I tested every single dev build for 4.3 to see which build introduced the regression going all the way back to the stable 4.2.2 and every single one of them fails to export with NativeAOT on and in release mode. However, when debug is on, the build works correctly. With NativeAOT off, debug and release modes work.

However, I don't think this changes that there may be a different error introduced in 4.3. This is because when I launch my project in 4.2.2 this is the error:

ERROR: Condition "!sdata.is_valid()" is true. Returning: nullptr
 at: instantiate (scene/resources/packed_scene.cpp:220)

This is obviously different than the two other errors above and it's still the same method that's failing which is when I load the packed scene for my top level Node3D and then call instantiate.

Here's a quick breakdown on how the errors change: