Bläddra i källkod

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

bruvzg 1 år sedan
förälder
incheckning
adc9def046
2 ändrade filer med 95 tillägg och 27 borttagningar
  1. 25 7
      include/godot_cpp/godot.hpp
  2. 70 20
      src/godot.cpp

+ 25 - 7
include/godot_cpp/godot.hpp

@@ -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);

+ 70 - 20
src/godot.cpp

@@ -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)
@@ -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) {
@@ -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