Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
284: Compatibility with 4.1 GDExtension API r=Bromeon a=Bromeon 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: ```cpp 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`: ```toml [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`: ```toml [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. Co-authored-by: Jan Haller <bromeon@gmail.com>
- Loading branch information