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

[iOS] Fix initialisation/termination of multiple statically linked extensions. #1316

Merged
merged 1 commit into from
Nov 24, 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
32 changes: 25 additions & 7 deletions include/godot_cpp/godot.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,26 +194,44 @@ enum ModuleInitializationLevel {
MODULE_INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
MODULE_INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS,
MODULE_INITIALIZATION_LEVEL_SCENE = GDEXTENSION_INITIALIZATION_SCENE,
MODULE_INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR
MODULE_INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR,
MODULE_INITIALIZATION_LEVEL_MAX
};

class GDExtensionBinding {
public:
using Callback = void (*)(ModuleInitializationLevel p_level);

static Callback init_callback;
static Callback terminate_callback;
static GDExtensionInitializationLevel minimum_initialization_level;
static GDExtensionBool init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);
struct InitData {
GDExtensionInitializationLevel minimum_initialization_level = GDEXTENSION_INITIALIZATION_CORE;
Callback init_callback = nullptr;
Callback terminate_callback = nullptr;
};

class InitDataList {
int data_count = 0;
int data_capacity = 0;
InitData **data = nullptr;

public:
void add(InitData *p_cb);
~InitDataList();
};

static bool api_initialized;
static int level_initialized[MODULE_INITIALIZATION_LEVEL_MAX];
static InitDataList initdata;
static GDExtensionBool init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, InitData *p_init_data, GDExtensionInitialization *r_initialization);

public:
static void initialize_level(void *userdata, GDExtensionInitializationLevel p_level);
static void deinitialize_level(void *userdata, GDExtensionInitializationLevel p_level);
static void initialize_level(void *p_userdata, GDExtensionInitializationLevel p_level);
static void deinitialize_level(void *p_userdata, GDExtensionInitializationLevel p_level);

class InitObject {
GDExtensionInterfaceGetProcAddress get_proc_address;
GDExtensionClassLibraryPtr library;
GDExtensionInitialization *initialization;
mutable InitData *init_data = nullptr;

public:
InitObject(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);
Expand Down
90 changes: 70 additions & 20 deletions src/godot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,9 @@ GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugi

} // namespace internal

GDExtensionBinding::Callback GDExtensionBinding::init_callback = nullptr;
GDExtensionBinding::Callback GDExtensionBinding::terminate_callback = nullptr;
GDExtensionInitializationLevel GDExtensionBinding::minimum_initialization_level = GDEXTENSION_INITIALIZATION_CORE;
bool GDExtensionBinding::api_initialized = false;
int GDExtensionBinding::level_initialized[MODULE_INITIALIZATION_LEVEL_MAX] = { 0 };
GDExtensionBinding::InitDataList GDExtensionBinding::initdata;

#define ERR_PRINT_EARLY(m_msg) \
internal::gdextension_interface_print_error(m_msg, FUNCTION_STR, __FILE__, __LINE__, false)
Expand All @@ -225,7 +225,20 @@ typedef struct {
GDExtensionInterfacePrintErrorWithMessage print_error_with_message;
} LegacyGDExtensionInterface;

GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, InitData *p_init_data, GDExtensionInitialization *r_initialization) {
if (!p_init_data || !p_init_data->init_callback) {
ERR_FAIL_V_MSG(false, "Initialization callback must be defined.");
}

if (api_initialized) {
r_initialization->initialize = initialize_level;
r_initialization->deinitialize = deinitialize_level;
r_initialization->userdata = p_init_data;
r_initialization->minimum_initialization_level = p_init_data->minimum_initialization_level;

return true;
}

// Make sure we weren't passed the legacy struct.
uint32_t *raw_interface = (uint32_t *)(void *)p_get_proc_address;
if (raw_interface[0] == 4 && raw_interface[1] == 0) {
Expand Down Expand Up @@ -415,59 +428,96 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge

r_initialization->initialize = initialize_level;
r_initialization->deinitialize = deinitialize_level;
r_initialization->minimum_initialization_level = minimum_initialization_level;

ERR_FAIL_NULL_V_MSG(init_callback, false, "Initialization callback must be defined.");
r_initialization->userdata = p_init_data;
r_initialization->minimum_initialization_level = p_init_data->minimum_initialization_level;

Variant::init_bindings();
godot::internal::register_engine_classes();

api_initialized = true;
return true;
}

#undef LOAD_PROC_ADDRESS
#undef ERR_PRINT_EARLY

void GDExtensionBinding::initialize_level(void *userdata, GDExtensionInitializationLevel p_level) {
void GDExtensionBinding::initialize_level(void *p_userdata, GDExtensionInitializationLevel p_level) {
ERR_FAIL_COND(static_cast<ModuleInitializationLevel>(p_level) >= MODULE_INITIALIZATION_LEVEL_MAX);
ClassDB::current_level = p_level;

if (init_callback) {
init_callback(static_cast<ModuleInitializationLevel>(p_level));
InitData *init_data = static_cast<InitData *>(p_userdata);
if (init_data && init_data->init_callback) {
init_data->init_callback(static_cast<ModuleInitializationLevel>(p_level));
}

ClassDB::initialize(p_level);
if (level_initialized[p_level] == 0) {
ClassDB::initialize(p_level);
}
level_initialized[p_level]++;
}

void GDExtensionBinding::deinitialize_level(void *userdata, GDExtensionInitializationLevel p_level) {
void GDExtensionBinding::deinitialize_level(void *p_userdata, GDExtensionInitializationLevel p_level) {
ERR_FAIL_COND(static_cast<ModuleInitializationLevel>(p_level) >= MODULE_INITIALIZATION_LEVEL_MAX);
ClassDB::current_level = p_level;

if (terminate_callback) {
terminate_callback(static_cast<ModuleInitializationLevel>(p_level));
InitData *init_data = static_cast<InitData *>(p_userdata);
if (init_data && init_data->terminate_callback) {
init_data->terminate_callback(static_cast<ModuleInitializationLevel>(p_level));
}

level_initialized[p_level]--;
if (level_initialized[p_level] == 0) {
EditorPlugins::deinitialize(p_level);
ClassDB::deinitialize(p_level);
}
}

void GDExtensionBinding::InitDataList::add(InitData *p_data) {
if (data_count == data_capacity) {
void *new_ptr = realloc(data, sizeof(InitData *) * (data_capacity + 32));
if (new_ptr) {
data = (InitData **)(new_ptr);
data_capacity += 32;
} else {
ERR_FAIL_MSG("Unable to allocate memory for extension callbacks.");
}
}
data[data_count++] = p_data;
}

EditorPlugins::deinitialize(p_level);
ClassDB::deinitialize(p_level);
GDExtensionBinding::InitDataList::~InitDataList() {
for (int i = 0; i < data_count; i++) {
if (data[i]) {
delete data[i];
}
}
if (data) {
free(data);
}
}

GDExtensionBinding::InitObject::InitObject(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
get_proc_address = p_get_proc_address;
library = p_library;
initialization = r_initialization;
init_data = new InitData();
GDExtensionBinding::initdata.add(init_data);
}

void GDExtensionBinding::InitObject::register_initializer(Callback p_init) const {
GDExtensionBinding::init_callback = p_init;
init_data->init_callback = p_init;
}

void GDExtensionBinding::InitObject::register_terminator(Callback p_terminate) const {
GDExtensionBinding::terminate_callback = p_terminate;
init_data->terminate_callback = p_terminate;
}

void GDExtensionBinding::InitObject::set_minimum_library_initialization_level(ModuleInitializationLevel p_level) const {
GDExtensionBinding::minimum_initialization_level = static_cast<GDExtensionInitializationLevel>(p_level);
init_data->minimum_initialization_level = static_cast<GDExtensionInitializationLevel>(p_level);
}

GDExtensionBool GDExtensionBinding::InitObject::init() const {
return GDExtensionBinding::init(get_proc_address, library, initialization);
return GDExtensionBinding::init(get_proc_address, library, init_data, initialization);
}

} // namespace godot