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

Compatibility with 4.1 GDExtension API #284

Merged
merged 13 commits into from
May 24, 2023
Merged

Compatibility with 4.1 GDExtension API #284

merged 13 commits into from
May 24, 2023

Conversation

Bromeon
Copy link
Member

@Bromeon Bromeon commented May 24, 2023

This pull request migrates gdext to the major GDExtension API change, as introduced in godotengine/godot#76406.
It also adopts to the new "uninitialized pointer" types, which were added in godotengine/godot#35813.

Doing so renders the CI green again, unblocking other PRs that have been broken by the upstream changes.


Major GDExtension API update

TLDR of Godot's changes: instead of one giant GDExtensionInterface struct with all the function pointers as data members, the C API now offers only a single function pointer get_proc_address. This one is passed to the entry point, in place of a pointer to the interface. With get_proc_address, it is possible to retrieve concrete API functions. In C++, this is done by looking up via name and casting the result to the correct function pointer type:

auto variant_call = (GDExtensionInterfaceVariantCall) get_proc_address("variant_call");
variant_call(...);

On the Rust side, I incorporated these changes by only modifying the FFI layer (godot-ffi crate), so the rest of the project is mostly unaffected. This is done by generating a GDExtensionInterface struct very similarly to how it existed before, in godot-codegen. As input, we parse the gdextension_interface.h header's documentation metadata. This works well so far, but we'll need to make sure with Godot devs that the doc metadata doesn't suddenly change format.


Uninitialized pointer types

Certain FFI functions construct objects "into an uninitialized pointer" (placement new in C++). There used to be quite some confusion regarding which functions do that. As a result, the C API introduced new types such as GDExtensionUninitializedStringPtr, which represent the uninitialized counterpart to GDExtensionStringPtr.

These typedefs are declared as void* in the C API, but we turn them into strongly typed opaque pointers by post-processing the C header. As such, it is impossible to accidentally mix initialized and uninitialized pointer types. I also added a trait AsUninit which allows explicit conversion between the two. This may not be necessary in the future, but is helpful until we are sure about correct usage everywhere. I marked some places that may need another look as // TODO(uninit).


Compatibility between Godot 4.0.x and 4.1+

Due to the API changes in GDExtension, extensions compiled under Godot 4.0.x are by default not compatible with Godot 4.1+ (which includes current master versions of Godot, so also our nightly CI builds).

From now on, the .gdextension interface must contain a new property compatibility_minimum:

[configuration]
entry_symbol = "gdext_rust_init"
compatibility_minimum = 4.0

[libraries]
linux.debug.x86_64 = "res://../../../target/debug/libdodge_the_creeps.so"
...

This value must be set to 4.0 if you compile against the old GDExtension API (current gdext uses 4.0.3 by default).
Set it to 4.1 if you use a Godot development version (Cargo feature custom-godot).
If you do this wrong, gdext will abort initialization and print a descriptive error message.

Compat bridge

Since breaking changes can be very disruptive, I built a bridging layer that allows to use existing compiled extensions under Godot v4.1 (or current development versions). Because the Rust code relies on certain features only available in the newer C header (e.g. uninitialized pointers), such changes were backported via dynamic post-processing of gdextension_interface.h.

Therefore, you don't need to mess around with custom-godot, just keep using gdext as-is and set compatibility_minimum = 4.0 to run it under newer engine versions. Developing against a specific GDExtension API is still possible via patch, the prebuilt artifacts have been updated for all 4.0.x versions. For example, to use version 4.0:

[patch."https://github.com/godot-rust/godot4-prebuilt"]
godot4-prebuilt = { git = "https://github.com//godot-rust/godot4-prebuilt", branch = "4.0"}

Once Godot 4.1.0 is released, we will likely make that the default version used by gdext, but I plan to maintain the compat layer as long as this is possible with reasonable effort.

A new CI setup verifies that the integration tests run against 4.0, 4.0.1, 4.0.2, 4.0.3 and nightly Godot versions, at least for Linux.
This will become a challenge as soon as there are breaking API changes (and there is already one upcoming in Basis::looking_at()).


Other changes

There is now an experimental godot::sys::GdextBuild struct, which provides informations about static (compiled-against) and runtime (loaded-by) Godot versions. This may be extended by other build/runtime metadata and will likely be promoted to an official API in the godot::init module.

Several memory leaks that came up during the change to uninitialized pointers were addressed. In many places, we now use from_sys_init() instead of from_sys_init_default(), avoiding the need to construct throwaway default instances.

In addition to more CI jobs for integration tests, there are now also 2 more linux-memcheck ones, to cover both 4.0.3 and nightly. This is mostly to have extra confidence during the migration phase and may be removed later.

Godot 4.0.3 is now the default version used by gdext.

@Bromeon Bromeon added feature Adds functionality to the library c: tooling CI, automation, tools c: ffi Low-level components and interaction with GDExtension API labels May 24, 2023
@Bromeon
Copy link
Member Author

Bromeon commented May 24, 2023

bors r+

@GodotRust
Copy link

API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-284

@bors
Copy link
Contributor

bors bot commented May 24, 2023

Build succeeded!

The publicly hosted instance of bors-ng is deprecated and will go away soon.

If you want to self-host your own instance, instructions are here.
For more help, visit the forum.

If you want to switch to GitHub's built-in merge queue, visit their help page.

@bors bors bot merged commit 81f81c6 into master May 24, 2023
@bors bors bot deleted the feature/godot41-compat branch May 24, 2023 18:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: ffi Low-level components and interaction with GDExtension API c: tooling CI, automation, tools feature Adds functionality to the library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants