From aed195f6dc5b149778768bad49faabd252819c11 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Fri, 14 Mar 2025 11:14:09 -0500 Subject: [PATCH] Register the classes used with the Godot editor --- gdextension/gdextension_interface.h | 24 ++++++++++++++++++++---- include/godot_cpp/core/class_db.hpp | 2 ++ include/godot_cpp/godot.hpp | 1 + src/core/class_db.cpp | 9 +++++++++ src/godot.cpp | 4 ++++ 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/gdextension/gdextension_interface.h b/gdextension/gdextension_interface.h index ac0181e0..107086fc 100644 --- a/gdextension/gdextension_interface.h +++ b/gdextension/gdextension_interface.h @@ -28,8 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef GDEXTENSION_INTERFACE_H -#define GDEXTENSION_INTERFACE_H +#pragma once /* This is a C class header, you can copy it and use it directly in your own binders. * Together with the JSON file, you should be able to generate any binder. @@ -401,6 +400,9 @@ typedef struct { typedef void *GDExtensionClassLibraryPtr; +/* Passed a pointer to a PackedStringArray that should be filled with the classes that may be used by the GDExtension. */ +typedef void (*GDExtensionEditorGetClassesUsedCallback)(GDExtensionTypePtr p_packed_string_array); + /* Method */ typedef enum { @@ -3078,8 +3080,22 @@ typedef void (*GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars)(const char * */ typedef void (*GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen)(const char *p_data, GDExtensionInt p_size); +/** + * @name editor_register_get_classes_used_callback + * @since 4.5 + * + * Registers a callback that Godot can call to get the list of all classes (from ClassDB) that may be used by the calling GDExtension. + * + * This is used by the editor to generate a build profiles (in "Tools" > "Engine Compilation Configuration Editor..." > "Detect from project"), + * in order to recompile Godot with only the classes used. + * In the provided callback, the GDExtension should provide the list of classes that _may_ be used statically, thus the time of invocation shouldn't matter. + * If a GDExtension doesn't register a callback, Godot will assume that it could be using any classes. + * + * @param p_library A pointer the library received by the GDExtension's entry point function. + * @param p_callback The callback to retrieve the list of classes used. + */ +typedef void (*GDExtensionInterfaceEditorRegisterGetClassesUsedCallback)(GDExtensionClassLibraryPtr p_library, GDExtensionEditorGetClassesUsedCallback p_callback); + #ifdef __cplusplus } #endif - -#endif // GDEXTENSION_INTERFACE_H diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp index ca20c44f..dca00744 100644 --- a/include/godot_cpp/core/class_db.hpp +++ b/include/godot_cpp/core/class_db.hpp @@ -168,6 +168,8 @@ public: instance_binding_callbacks[p_name] = p_callbacks; } + static void _editor_get_classes_used_callback(GDExtensionTypePtr p_packed_string_array); + static void _register_engine_singleton(const StringName &p_class_name, Object *p_singleton) { std::lock_guard lock(engine_singletons_mutex); std::unordered_map::const_iterator i = engine_singletons.find(p_class_name); diff --git a/include/godot_cpp/godot.hpp b/include/godot_cpp/godot.hpp index 40693aa7..1a0bef9d 100644 --- a/include/godot_cpp/godot.hpp +++ b/include/godot_cpp/godot.hpp @@ -198,6 +198,7 @@ extern "C" GDExtensionInterfaceClassdbUnregisterExtensionClass gdextension_inter extern "C" GDExtensionInterfaceGetLibraryPath gdextension_interface_get_library_path; extern "C" GDExtensionInterfaceEditorAddPlugin gdextension_interface_editor_add_plugin; extern "C" GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugin; +extern "C" GDExtensionInterfaceEditorRegisterGetClassesUsedCallback gdextension_interface_editor_register_get_classes_used_callback; extern "C" GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars gdextension_interface_editor_help_load_xml_from_utf8_chars; extern "C" GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen gdextension_interface_editor_help_load_xml_from_utf8_chars_and_len; extern "C" GDExtensionInterfaceImagePtrw gdextension_interface_image_ptrw; diff --git a/src/core/class_db.cpp b/src/core/class_db.cpp index f6257b73..854a12f4 100644 --- a/src/core/class_db.cpp +++ b/src/core/class_db.cpp @@ -388,6 +388,15 @@ void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_ } } +void ClassDB::_editor_get_classes_used_callback(GDExtensionTypePtr p_packed_string_array) { + PackedStringArray *arr = reinterpret_cast(p_packed_string_array); + arr->resize(instance_binding_callbacks.size()); + int index = 0; + for (const std::pair &pair : instance_binding_callbacks) { + (*arr)[index++] = pair.first; + } +} + void ClassDB::initialize_class(const ClassInfo &p_cl) { } diff --git a/src/godot.cpp b/src/godot.cpp index 31a64c46..add05e14 100644 --- a/src/godot.cpp +++ b/src/godot.cpp @@ -204,6 +204,7 @@ GDExtensionInterfaceClassdbUnregisterExtensionClass gdextension_interface_classd GDExtensionInterfaceGetLibraryPath gdextension_interface_get_library_path = nullptr; GDExtensionInterfaceEditorAddPlugin gdextension_interface_editor_add_plugin = nullptr; GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugin = nullptr; +GDExtensionInterfaceEditorRegisterGetClassesUsedCallback gdextension_interface_editor_register_get_classes_used_callback = nullptr; GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars gdextension_interface_editor_help_load_xml_from_utf8_chars = nullptr; GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen gdextension_interface_editor_help_load_xml_from_utf8_chars_and_len = nullptr; GDExtensionInterfaceImagePtrw gdextension_interface_image_ptrw = nullptr; @@ -486,6 +487,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge LOAD_PROC_ADDRESS(get_library_path, GDExtensionInterfaceGetLibraryPath); LOAD_PROC_ADDRESS(editor_add_plugin, GDExtensionInterfaceEditorAddPlugin); LOAD_PROC_ADDRESS(editor_remove_plugin, GDExtensionInterfaceEditorRemovePlugin); + LOAD_PROC_ADDRESS(editor_register_get_classes_used_callback, GDExtensionInterfaceEditorRegisterGetClassesUsedCallback); LOAD_PROC_ADDRESS(editor_help_load_xml_from_utf8_chars, GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars); LOAD_PROC_ADDRESS(editor_help_load_xml_from_utf8_chars_and_len, GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen); LOAD_PROC_ADDRESS(image_ptrw, GDExtensionInterfaceImagePtrw); @@ -521,6 +523,8 @@ void GDExtensionBinding::initialize_level(void *p_userdata, GDExtensionInitializ level_initialized[p_level]++; if ((ModuleInitializationLevel)p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { + internal::gdextension_interface_editor_register_get_classes_used_callback(internal::library, &ClassDB::_editor_get_classes_used_callback); + const internal::DocData &doc_data = internal::get_doc_data(); if (doc_data.is_valid()) { doc_data.load_data();