You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-05 12:10:55 +00:00
Merge pull request #106030 from raulsntos/gdextension/main-loop-callbacks
[GDExtension] Add function to register main loop callbacks
This commit is contained in:
@@ -694,6 +694,13 @@ void GDExtension::_register_get_classes_used_callback(GDExtensionClassLibraryPtr
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GDExtension::_register_main_loop_callbacks(GDExtensionClassLibraryPtr p_library, const GDExtensionMainLoopCallbacks *p_callbacks) {
|
||||||
|
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
|
||||||
|
self->startup_callback = p_callbacks->startup_func;
|
||||||
|
self->shutdown_callback = p_callbacks->shutdown_func;
|
||||||
|
self->frame_callback = p_callbacks->frame_func;
|
||||||
|
}
|
||||||
|
|
||||||
void GDExtension::register_interface_function(const StringName &p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) {
|
void GDExtension::register_interface_function(const StringName &p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) {
|
||||||
ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), vformat("Attempt to register interface function '%s', which appears to be already registered.", p_function_name));
|
ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), vformat("Attempt to register interface function '%s', which appears to be already registered.", p_function_name));
|
||||||
gdextension_interface_functions.insert(p_function_name, p_function_pointer);
|
gdextension_interface_functions.insert(p_function_name, p_function_pointer);
|
||||||
@@ -813,6 +820,7 @@ void GDExtension::initialize_gdextensions() {
|
|||||||
register_interface_function("classdb_unregister_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_unregister_extension_class);
|
register_interface_function("classdb_unregister_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_unregister_extension_class);
|
||||||
register_interface_function("get_library_path", (GDExtensionInterfaceFunctionPtr)&GDExtension::_get_library_path);
|
register_interface_function("get_library_path", (GDExtensionInterfaceFunctionPtr)&GDExtension::_get_library_path);
|
||||||
register_interface_function("editor_register_get_classes_used_callback", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_get_classes_used_callback);
|
register_interface_function("editor_register_get_classes_used_callback", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_get_classes_used_callback);
|
||||||
|
register_interface_function("register_main_loop_callbacks", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_main_loop_callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDExtension::finalize_gdextensions() {
|
void GDExtension::finalize_gdextensions() {
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ class GDExtension : public Resource {
|
|||||||
static void _unregister_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name);
|
static void _unregister_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name);
|
||||||
static void _get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionStringPtr r_path);
|
static void _get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionStringPtr r_path);
|
||||||
static void _register_get_classes_used_callback(GDExtensionClassLibraryPtr p_library, GDExtensionEditorGetClassesUsedCallback p_callback);
|
static void _register_get_classes_used_callback(GDExtensionClassLibraryPtr p_library, GDExtensionEditorGetClassesUsedCallback p_callback);
|
||||||
|
static void _register_main_loop_callbacks(GDExtensionClassLibraryPtr p_library, const GDExtensionMainLoopCallbacks *p_callbacks);
|
||||||
|
|
||||||
GDExtensionInitialization initialization;
|
GDExtensionInitialization initialization;
|
||||||
int32_t level_initialized = -1;
|
int32_t level_initialized = -1;
|
||||||
@@ -115,6 +116,10 @@ class GDExtension : public Resource {
|
|||||||
void clear_instance_bindings();
|
void clear_instance_bindings();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
GDExtensionMainLoopStartupCallback startup_callback = nullptr;
|
||||||
|
GDExtensionMainLoopShutdownCallback shutdown_callback = nullptr;
|
||||||
|
GDExtensionMainLoopFrameCallback frame_callback = nullptr;
|
||||||
|
|
||||||
static inline HashMap<StringName, GDExtensionInterfaceFunctionPtr> gdextension_interface_functions;
|
static inline HashMap<StringName, GDExtensionInterfaceFunctionPtr> gdextension_interface_functions;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -805,6 +805,21 @@ typedef struct {
|
|||||||
const char *string; // (e.g. "Godot v3.1.4.stable.official.mono")
|
const char *string; // (e.g. "Godot v3.1.4.stable.official.mono")
|
||||||
} GDExtensionGodotVersion2;
|
} GDExtensionGodotVersion2;
|
||||||
|
|
||||||
|
/* Called when starting the main loop. */
|
||||||
|
typedef void (*GDExtensionMainLoopStartupCallback)();
|
||||||
|
|
||||||
|
/* Called when shutting down the main loop. */
|
||||||
|
typedef void (*GDExtensionMainLoopShutdownCallback)();
|
||||||
|
|
||||||
|
/* Called for every frame iteration of the main loop. */
|
||||||
|
typedef void (*GDExtensionMainLoopFrameCallback)();
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GDExtensionMainLoopStartupCallback startup_func;
|
||||||
|
GDExtensionMainLoopShutdownCallback shutdown_func;
|
||||||
|
GDExtensionMainLoopFrameCallback frame_func;
|
||||||
|
} GDExtensionMainLoopCallbacks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name get_godot_version
|
* @name get_godot_version
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
@@ -3132,6 +3147,17 @@ typedef void (*GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen)(const
|
|||||||
*/
|
*/
|
||||||
typedef void (*GDExtensionInterfaceEditorRegisterGetClassesUsedCallback)(GDExtensionClassLibraryPtr p_library, GDExtensionEditorGetClassesUsedCallback p_callback);
|
typedef void (*GDExtensionInterfaceEditorRegisterGetClassesUsedCallback)(GDExtensionClassLibraryPtr p_library, GDExtensionEditorGetClassesUsedCallback p_callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name register_main_loop_callbacks
|
||||||
|
* @since 4.5
|
||||||
|
*
|
||||||
|
* Registers callbacks to be called at different phases of the main loop.
|
||||||
|
*
|
||||||
|
* @param p_library A pointer the library received by the GDExtension's entry point function.
|
||||||
|
* @param p_callback A pointer to the structure that contains the callbacks.
|
||||||
|
*/
|
||||||
|
typedef void (*GDExtensionInterfaceRegisterMainLoopCallbacks)(GDExtensionClassLibraryPtr p_library, const GDExtensionMainLoopCallbacks *p_callbacks);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -60,6 +60,14 @@ GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(cons
|
|||||||
emit_signal("extension_loaded", p_extension);
|
emit_signal("extension_loaded", p_extension);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (startup_callback_called) {
|
||||||
|
// Extension is loading after the startup callback has already been called,
|
||||||
|
// so we call it now for this extension to make sure it doesn't miss it.
|
||||||
|
if (p_extension->startup_callback) {
|
||||||
|
p_extension->startup_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return LOAD_STATUS_OK;
|
return LOAD_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,10 +84,24 @@ GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!shutdown_callback_called) {
|
||||||
|
// Extension is unloading before the shutdown callback has been called,
|
||||||
|
// which means the engine hasn't shutdown yet but we want to make sure
|
||||||
|
// to call the shutdown callback so it doesn't miss it.
|
||||||
|
if (p_extension->shutdown_callback) {
|
||||||
|
p_extension->shutdown_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const KeyValue<String, String> &kv : p_extension->class_icon_paths) {
|
for (const KeyValue<String, String> &kv : p_extension->class_icon_paths) {
|
||||||
gdextension_class_icon_paths.erase(kv.key);
|
gdextension_class_icon_paths.erase(kv.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear main loop callbacks.
|
||||||
|
p_extension->startup_callback = nullptr;
|
||||||
|
p_extension->shutdown_callback = nullptr;
|
||||||
|
p_extension->frame_callback = nullptr;
|
||||||
|
|
||||||
return LOAD_STATUS_OK;
|
return LOAD_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,6 +409,37 @@ bool GDExtensionManager::ensure_extensions_loaded(const HashSet<String> &p_exten
|
|||||||
return needs_restart;
|
return needs_restart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GDExtensionManager::startup() {
|
||||||
|
startup_callback_called = true;
|
||||||
|
|
||||||
|
for (const KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
|
||||||
|
const Ref<GDExtension> &extension = E.value;
|
||||||
|
if (extension->startup_callback) {
|
||||||
|
extension->startup_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDExtensionManager::shutdown() {
|
||||||
|
shutdown_callback_called = true;
|
||||||
|
|
||||||
|
for (const KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
|
||||||
|
const Ref<GDExtension> &extension = E.value;
|
||||||
|
if (extension->shutdown_callback) {
|
||||||
|
extension->shutdown_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDExtensionManager::frame() {
|
||||||
|
for (const KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
|
||||||
|
const Ref<GDExtension> &extension = E.value;
|
||||||
|
if (extension->frame_callback) {
|
||||||
|
extension->frame_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GDExtensionManager *GDExtensionManager::get_singleton() {
|
GDExtensionManager *GDExtensionManager::get_singleton() {
|
||||||
return singleton;
|
return singleton;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ class GDExtensionManager : public Object {
|
|||||||
HashMap<String, Ref<GDExtension>> gdextension_map;
|
HashMap<String, Ref<GDExtension>> gdextension_map;
|
||||||
HashMap<String, String> gdextension_class_icon_paths;
|
HashMap<String, String> gdextension_class_icon_paths;
|
||||||
|
|
||||||
|
bool startup_callback_called = false;
|
||||||
|
bool shutdown_callback_called = false;
|
||||||
|
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
static inline GDExtensionManager *singleton = nullptr;
|
static inline GDExtensionManager *singleton = nullptr;
|
||||||
@@ -86,6 +89,10 @@ public:
|
|||||||
void reload_extensions();
|
void reload_extensions();
|
||||||
bool ensure_extensions_loaded(const HashSet<String> &p_extensions);
|
bool ensure_extensions_loaded(const HashSet<String> &p_extensions);
|
||||||
|
|
||||||
|
void startup();
|
||||||
|
void shutdown();
|
||||||
|
void frame();
|
||||||
|
|
||||||
GDExtensionManager();
|
GDExtensionManager();
|
||||||
~GDExtensionManager();
|
~GDExtensionManager();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4583,6 +4583,8 @@ int Main::start() {
|
|||||||
movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, Engine::get_singleton()->get_write_movie_path());
|
movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, Engine::get_singleton()->get_write_movie_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GDExtensionManager::get_singleton()->startup();
|
||||||
|
|
||||||
if (minimum_time_msec) {
|
if (minimum_time_msec) {
|
||||||
uint64_t minimum_time = 1000 * minimum_time_msec;
|
uint64_t minimum_time = 1000 * minimum_time_msec;
|
||||||
uint64_t elapsed_time = OS::get_singleton()->get_ticks_usec();
|
uint64_t elapsed_time = OS::get_singleton()->get_ticks_usec();
|
||||||
@@ -4788,6 +4790,8 @@ bool Main::iteration() {
|
|||||||
process_max = MAX(process_ticks, process_max);
|
process_max = MAX(process_ticks, process_max);
|
||||||
uint64_t frame_time = OS::get_singleton()->get_ticks_usec() - ticks;
|
uint64_t frame_time = OS::get_singleton()->get_ticks_usec() - ticks;
|
||||||
|
|
||||||
|
GDExtensionManager::get_singleton()->frame();
|
||||||
|
|
||||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||||
ScriptServer::get_language(i)->frame();
|
ScriptServer::get_language(i)->frame();
|
||||||
}
|
}
|
||||||
@@ -4906,6 +4910,8 @@ void Main::cleanup(bool p_force) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
GDExtensionManager::get_singleton()->shutdown();
|
||||||
|
|
||||||
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
|
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
|
||||||
TextServerManager::get_singleton()->get_interface(i)->cleanup();
|
TextServerManager::get_singleton()->get_interface(i)->cleanup();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user