Browse Source

Merge pull request #1743 from dsnopek/get-used-classes

Register the classes used with the Godot editor
David Snopek 3 months ago
parent
commit
08fd033a9e

+ 20 - 4
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

+ 2 - 0
include/godot_cpp/core/class_db.hpp

@@ -167,6 +167,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<std::mutex> lock(engine_singletons_mutex);
 		std::unordered_map<StringName, Object *>::const_iterator i = engine_singletons.find(p_class_name);

+ 1 - 0
include/godot_cpp/godot.hpp

@@ -197,6 +197,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;

+ 9 - 0
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<PackedStringArray *>(p_packed_string_array);
+	arr->resize(instance_binding_callbacks.size());
+	int index = 0;
+	for (const std::pair<StringName, const GDExtensionInstanceBindingCallbacks *> &pair : instance_binding_callbacks) {
+		(*arr)[index++] = pair.first;
+	}
+}
+
 void ClassDB::initialize_class(const ClassInfo &p_cl) {
 }
 

+ 4 - 0
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();