From 329fff69ea6f63f8304a0d3670cce1b4e067c26f Mon Sep 17 00:00:00 2001 From: Joshua Pedrick Date: Thu, 4 May 2023 23:03:15 -0400 Subject: [PATCH] #66231 enable detection of gdextension library change for editor restart and experimental work on dynamic reload --- core/extension/gdextension.cpp | 22 +++++++++++ core/extension/gdextension.h | 2 + core/extension/gdextension_manager.cpp | 53 ++++++++++++++++++++++---- core/object/class_db.cpp | 8 ++++ editor/editor_file_system.cpp | 11 ++++-- 5 files changed, 86 insertions(+), 10 deletions(-) diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 4187c15e84d5..9463a30079aa 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -245,8 +245,11 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library StringName class_name = *reinterpret_cast(p_class_name); StringName parent_class_name = *reinterpret_cast(p_parent_class_name); + ERR_FAIL_COND_MSG(!String(class_name).is_valid_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier."); +#ifndef SUPPORT_DYNAMIC_GDEXTENSION_RELOAD ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered."); +#endif Extension *parent_extension = nullptr; @@ -381,6 +384,7 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra Extension *ext = &self->extension_classes[class_name]; ERR_FAIL_COND_MSG(ext->gdextension.children.size(), "Attempt to unregister class '" + class_name + "' while other extension classes inherit from it."); + OS::get_singleton()->print("Unregistering extension class %s\n",String(class_name).utf8().get_data()); ClassDB::unregister_extension_class(class_name); if (ext->gdextension.parent != nullptr) { ext->gdextension.parent->children.erase(&ext->gdextension); @@ -394,6 +398,21 @@ void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExte *(String *)r_path = self->library_path; } +Error GDExtension::reload(){ + auto old_library = library; + + library = nullptr; + + auto status = open_library(library_path, entry_symbol); + OS::get_singleton()->print("Opened new, closing old: %s %p\n",String(library_path).utf8().get_data(), old_library); + if( old_library ){ + auto close_status = OS::get_singleton()->close_dynamic_library(old_library); + ERR_FAIL_COND_V_MSG(close_status != Error::OK, close_status, "Can't close dynamic library: " + library_path + ", error:."); + } + + return status; +} + Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol) { Error err = OS::get_singleton()->open_dynamic_library(p_path, library, true, &library_path); if (err != OK) { @@ -415,6 +434,9 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb if (initialization_function(&gdextension_interface, this, &initialization)) { level_initialized = -1; + modified_time = FileAccess::get_modified_time(library_path); + entry_symbol = p_entry_symbol; + return OK; } else { ERR_PRINT("GDExtension initialization function '" + p_entry_symbol + "' returned an error."); diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index 743bfe4ae265..5a078f20c516 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -43,6 +43,7 @@ class GDExtension : public Resource { void *library = nullptr; // pointer if valid, String library_path; + String entry_symbol; struct Extension { ObjectGDExtension gdextension; @@ -75,6 +76,7 @@ class GDExtension : public Resource { Error open_library(const String &p_path, const String &p_entry_symbol); void close_library(); + Error reload(); enum InitializationLevel { INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE, diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp index 6f11994dd687..c08210165a46 100644 --- a/core/extension/gdextension_manager.cpp +++ b/core/extension/gdextension_manager.cpp @@ -60,14 +60,53 @@ GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String & } GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String &p_path) { - if ( gdextension_map.has(p_path) ){ - return GDExtensionManager::LOAD_STATUS_NEEDS_RESTART; - //unload_extension(p_path); - //return load_extension(p_path); - } - else{ - return LOAD_STATUS_NOT_LOADED; +#ifndef SUPPORT_DYNAMIC_GDEXTENSION_RELOAD + return LOAD_STATUS_NEEDS_RESTART; +#else + auto extension_it = gdextension_map.find(p_path); + if (extension_it != gdextension_map.end()){ + Ref extension = extension_it->value; + + auto reload_status = extension->reload(); + if (reload_status != Error::OK){ + return LOAD_STATUS_NEEDS_RESTART; + } + + if (level >= 0) { // Already initialized up to some level. + int32_t minimum_level = extension->get_minimum_library_initialization_level(); + if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) { + return LOAD_STATUS_NEEDS_RESTART; + } + // Deinitialize down to current level. + for (int32_t i = level; i >= minimum_level; i--) { + extension->deinitialize_library(GDExtension::InitializationLevel(i)); + } + } + + for (const KeyValue &kv : extension->class_icon_paths) { + gdextension_class_icon_paths.erase(kv.key); + } + + if (level >= 0) { // Already initialized up to some level. + int32_t minimum_level = extension->get_minimum_library_initialization_level(); + if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) { + return LOAD_STATUS_NEEDS_RESTART; + } + // Initialize up to current level. + for (int32_t i = minimum_level; i <= level; i++) { + extension->initialize_library(GDExtension::InitializationLevel(i)); + } + } + + for (const KeyValue &kv : extension->class_icon_paths) { + gdextension_class_icon_paths[kv.key] = kv.value; + } + + return LOAD_STATUS_OK; } + + return LOAD_STATUS_NOT_LOADED; +#endif } GDExtensionManager::LoadStatus GDExtensionManager::unload_extension(const String &p_path) { diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 760f3bfd0c9b..f2dfb0c66611 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -34,6 +34,7 @@ #include "core/object/script_language.h" #include "core/os/mutex.h" #include "core/version.h" +#include "core/os/os.h" #define OBJTYPE_RLOCK RWLockRead _rw_lockr_(lock); #define OBJTYPE_WLOCK RWLockWrite _rw_lockw_(lock); @@ -1538,9 +1539,16 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con void ClassDB::register_extension_class(ObjectGDExtension *p_extension) { GLOBAL_LOCK_FUNCTION; +#ifndef SUPPORT_DYNAMIC_GDEXTENSION_RELOAD ERR_FAIL_COND_MSG(classes.has(p_extension->class_name), "Class already registered: " + String(p_extension->class_name)); +#else + if (classes.has(p_extension->class_name)){ + unregister_extension_class(p_extension->class_name); + } +#endif ERR_FAIL_COND_MSG(!classes.has(p_extension->parent_class_name), "Parent class name for extension class not found: " + String(p_extension->parent_class_name)); + ClassInfo *parent = classes.getptr(p_extension->parent_class_name); ClassInfo c; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index e0ab44447a02..9ebda854657b 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -2518,13 +2518,18 @@ bool EditorFileSystem::_scan_extensions() { } for (const auto& E : extensions_changed){ - needs_restart = true; + OS::get_singleton()->print("Extension %s changed, needs reload.\n", E.utf8().get_data()); GDExtensionManager::LoadStatus st = em->reload_extension(E); - if ( st == GDExtensionManager::LOAD_STATUS_FAILED ){ - EditorNode::get_singleton()->add_io_error("Error removing extension: " + E); + if (st == GDExtensionManager::LOAD_STATUS_FAILED){ + ERR_PRINT("Error loading GDExtension: " + E); + EditorNode::get_singleton()->add_io_error("Error reloading extension: " + E); } else if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) { + WARN_PRINT("GDExtension changed, need restart: " + E); needs_restart = true; + } else if(st == GDExtensionManager::LOAD_STATUS_OK) { + OS::get_singleton()->print("Extension %s reload OK.\n",E.utf8().get_data()); } + } return needs_restart;