Skip to content

Rework GDExtension interface from a struct to loading function pointers #76406

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

Merged
merged 1 commit into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 84 additions & 20 deletions core/extension/gdextension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@
#include "core/object/class_db.h"
#include "core/object/method_bind.h"
#include "core/os/os.h"
#include "core/version.h"

extern void gdextension_setup_interface();
extern void *gdextension_get_legacy_interface();
extern GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name);

typedef GDExtensionBool (*GDExtensionLegacyInitializationFunction)(void *p_interface, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);

String GDExtension::get_extension_list_config_file() {
return ProjectSettings::get_singleton()->get_project_data_path().path_join("extension_list.cfg");
Expand Down Expand Up @@ -275,8 +282,6 @@ class GDExtensionMethodBind : public MethodBind {
}
};

static GDExtensionInterface gdextension_interface;

void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) {
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);

Expand Down Expand Up @@ -431,7 +436,20 @@ void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExte
memnew_placement(r_path, String(self->library_path));
}

Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol) {
HashMap<StringName, GDExtensionInterfaceFunctionPtr> gdextension_interface_functions;

void GDExtension::register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) {
ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), "Attempt to register interface function '" + p_function_name + "', which appears to be already registered.");
gdextension_interface_functions.insert(p_function_name, p_function_pointer);
}

GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(StringName p_function_name) {
GDExtensionInterfaceFunctionPtr *function = gdextension_interface_functions.getptr(p_function_name);
ERR_FAIL_COND_V_MSG(function == nullptr, nullptr, "Attempt to get non-existent interface function: " + p_function_name);
return *function;
}

Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol, bool p_use_legacy_interface) {
Error err = OS::get_singleton()->open_dynamic_library(p_path, library, true, &library_path);
if (err != OK) {
ERR_PRINT("GDExtension dynamic library not found: " + p_path);
Expand All @@ -448,9 +466,17 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
return err;
}

GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
GDExtensionBool ret = 0;
if (p_use_legacy_interface) {
GDExtensionLegacyInitializationFunction initialization_function = (GDExtensionLegacyInitializationFunction)entry_funcptr;
ret = initialization_function(gdextension_get_legacy_interface(), this, &initialization);

} else {
GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
ret = initialization_function(&gdextension_get_proc_address, this, &initialization);
}

if (initialization_function(&gdextension_interface, this, &initialization)) {
if (ret) {
level_initialized = -1;
return OK;
} else {
Expand All @@ -459,6 +485,10 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
}
}

Error GDExtension::open_library_compat_76406(const String &p_path, const String &p_entry_symbol) {
return open_library(p_path, p_entry_symbol, true);
}

void GDExtension::close_library() {
ERR_FAIL_COND(library == nullptr);
OS::get_singleton()->close_dynamic_library(library);
Expand Down Expand Up @@ -494,7 +524,8 @@ void GDExtension::deinitialize_library(InitializationLevel p_level) {
}

void GDExtension::_bind_methods() {
ClassDB::bind_method(D_METHOD("open_library", "path", "entry_symbol"), &GDExtension::open_library);
ClassDB::bind_method(D_METHOD("open_library", "path", "entry_symbol", "use_legacy_interface"), &GDExtension::open_library);
ClassDB::bind_compatibility_method(D_METHOD("open_library", "path", "entry_symbol"), &GDExtension::open_library_compat_76406);
ClassDB::bind_method(D_METHOD("close_library"), &GDExtension::close_library);
ClassDB::bind_method(D_METHOD("is_library_open"), &GDExtension::is_library_open);

Expand All @@ -516,20 +547,18 @@ GDExtension::~GDExtension() {
}
}

extern void gdextension_setup_interface(GDExtensionInterface *p_interface);

void GDExtension::initialize_gdextensions() {
gdextension_setup_interface(&gdextension_interface);

gdextension_interface.classdb_register_extension_class = _register_extension_class;
gdextension_interface.classdb_register_extension_class_method = _register_extension_class_method;
gdextension_interface.classdb_register_extension_class_integer_constant = _register_extension_class_integer_constant;
gdextension_interface.classdb_register_extension_class_property = _register_extension_class_property;
gdextension_interface.classdb_register_extension_class_property_group = _register_extension_class_property_group;
gdextension_interface.classdb_register_extension_class_property_subgroup = _register_extension_class_property_subgroup;
gdextension_interface.classdb_register_extension_class_signal = _register_extension_class_signal;
gdextension_interface.classdb_unregister_extension_class = _unregister_extension_class;
gdextension_interface.get_library_path = _get_library_path;
gdextension_setup_interface();

register_interface_function("classdb_register_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class);
register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method);
register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant);
register_interface_function("classdb_register_extension_class_property", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property);
register_interface_function("classdb_register_extension_class_property_group", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_group);
register_interface_function("classdb_register_extension_class_property_subgroup", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_subgroup);
register_interface_function("classdb_register_extension_class_signal", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_signal);
register_interface_function("classdb_unregister_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_unregister_extension_class);
register_interface_function("get_library_path", (GDExtensionInterfaceFunctionPtr)&GDExtension::_get_library_path);
}

Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Expand Down Expand Up @@ -557,6 +586,39 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String

String entry_symbol = config->get_value("configuration", "entry_symbol");

uint32_t compatibility_minimum[3] = { 0, 0, 0 };
if (config->has_section_key("configuration", "compatibility_minimum")) {
String compat_string = config->get_value("configuration", "compatibility_minimum");
Vector<int> parts = compat_string.split_ints(".");
for (int i = 0; i < parts.size(); i++) {
if (i >= 3) {
break;
}
if (parts[i] >= 0) {
compatibility_minimum[i] = parts[i];
}
}
}
if (compatibility_minimum[0] < 4) {
compatibility_minimum[0] = 4;
}

bool compatible = true;
if (VERSION_MAJOR < compatibility_minimum[0]) {
compatible = false;
} else if (VERSION_MINOR < compatibility_minimum[1]) {
compatible = false;
} else if (VERSION_PATCH < compatibility_minimum[2]) {
compatible = false;
}
if (!compatible) {
if (r_error) {
*r_error = ERR_INVALID_DATA;
}
ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
return Ref<Resource>();
}

String library_path = GDExtension::find_extension_library(p_path, config, [](String p_feature) { return OS::get_singleton()->has_feature(p_feature); });

if (library_path.is_empty()) {
Expand All @@ -572,10 +634,12 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String
library_path = p_path.get_base_dir().path_join(library_path);
}

bool use_legacy_interface = compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0;

Ref<GDExtension> lib;
lib.instantiate();
String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path);
err = lib->open_library(abs_path, entry_symbol);
err = lib->open_library(abs_path, entry_symbol, use_legacy_interface);

if (r_error) {
*r_error = err;
Expand Down
6 changes: 5 additions & 1 deletion core/extension/gdextension.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ class GDExtension : public Resource {
static String get_extension_list_config_file();
static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);

Error open_library(const String &p_path, const String &p_entry_symbol);
Error open_library(const String &p_path, const String &p_entry_symbol, bool p_use_legacy_interface = false);
Error open_library_compat_76406(const String &p_path, const String &p_entry_symbol);
void close_library();

enum InitializationLevel {
Expand All @@ -88,7 +89,10 @@ class GDExtension : public Resource {
void initialize_library(InitializationLevel p_level);
void deinitialize_library(InitializationLevel p_level);

static void register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer);
static GDExtensionInterfaceFunctionPtr get_interface_function(StringName p_function_name);
static void initialize_gdextensions();

GDExtension();
~GDExtension();
};
Expand Down
Loading