Skip to content

Singletons such as RenderingServer should be accessible in MODULE_INITIALIZATION_LEVEL_SCENE #1180

@Zylann

Description

@Zylann

Godot version

4.1

godot-cpp version

4.1

System information

Windows 10 64 bits NVIDIA GeForce GTX 1060

Issue description

RenderingServer::get_singleton returns null in cases where it should not.

  • If you have code in your extension library initializer that tries to access RenderingServer, it will crash because that singleton will be null, even in initialization levels that should definitely work in modules (like MODULE_INITIALIZATION_LEVEL_SCENE).

  • You can't even check that get_singleton returns nullptr, because the getter itself is doing something that crashes when the singleton is null, in release builds:

    result.append("#ifdef DEBUG_ENABLED")

  • Similarly, if you have code in a class constructor (node, resource...) that tries to access RenderingServer, it will also crash the editor on startup, because EditorHelp creates temporary instances of every class in ClassDB to access the default values of their properties (mentionned in There should be a way to register classes without exposing them (usually editor plugin internals) #1179).

  • In fact, if you dare calling RenderingServer::get_singleton() and it returns null, it will keep returning null forever, because in GodotCpp the wrapper caches the value once in a static variable and never runs again:

    "\tstatic GDExtensionObjectPtr singleton_obj = internal::gdextension_interface_global_get_singleton(_gde_class_name._native_ptr());"

This is also the case with other servers such as PhysicsServer.

In all the mentionned cases, the server singleton actually exists.
Apparently the cause during initialization is that singletons in GDExtension are accessed similarly to scripts, which is terrible because that API is initialized way too late and completely disregards MODULE_INITIALIZATION_ levels.
This is very confusing and impractical. GDExtension needs the same access API modules have.

I notably work on a big module project, which can also compile as a GDExtension, so finding workarounds gets more complicated.

Steps to reproduce

Access RenderingServer::get_singleton() inside your library initializer, in MODULE_INITIALIZATION_LEVEL_SCENE, or access it inside the constructor of a custom class.

Minimal reproduction project

N.A

Alternative (theory)

This came up in a meeting: in modules, Godot is currently mixing up registration (declaring that X is there, but not executing anything) and initialization in modules. And by extension... extensions too 😁 In an ideal world, these two steps should be very separate.

In my module, I don't actually need to initialize singletons in MODULE_INITIALIZATION_LEVEL_SCENE, I just do it there because in the present state of things, I can do it there. But in GDExtension there is nowhere I can do it which won't cause the present issue.

Though regardless of this idea, there would still be a need to be able to order each step, because registration can depend on other things (base classes?), and initialization can also depend on others (accessing a core singleton when initializing a custom singleton).

Workarounds

  • Autoload: if this situations happens due to initializing a singleton, you could make it an autoload node. The downside is that users have to set it up.
  • Lazy-init: when some logic runs at a time you know the server is available (like _ready in a custom node), add a lazy-init checking a global boolean. If false, set it to true and do the initialization. If that initialization is heavy and the code runs a bit late in the app's cycle, it might cause a noticeable hitch.
  • Use callable_mp_static(&my_setup_function).call_deferred(); from [MODULE_INITIALIZATION_* callback. There is no guarantee when that will actually run, but there is a good chance it will be after singletons have been made accessible.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThis has been identified as a bugtopic:gdextensionThis relates to the new Godot 4 extension implementation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions