1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-05 12:10:55 +00:00

Merge pull request #80284 from dsnopek/gdextension-hot-reload

Implement reloading of GDExtensions
This commit is contained in:
Rémi Verschelde
2023-09-26 08:17:28 +02:00
16 changed files with 805 additions and 89 deletions

View File

@@ -31,6 +31,7 @@
#include "object.h"
#include "core/core_string_names.h"
#include "core/extension/gdextension_manager.h"
#include "core/io/resource.h"
#include "core/object/class_db.h"
#include "core/object/message_queue.h"
@@ -1794,14 +1795,17 @@ StringName Object::get_class_name_for_extension(const GDExtension *p_library) co
}
void Object::set_instance_binding(void *p_token, void *p_binding, const GDExtensionInstanceBindingCallbacks *p_callbacks) {
// This is only meant to be used on creation by the binder.
ERR_FAIL_COND(_instance_bindings != nullptr);
_instance_bindings = (InstanceBinding *)memalloc(sizeof(InstanceBinding));
// This is only meant to be used on creation by the binder, but we also
// need to account for reloading (where the 'binding' will be cleared).
ERR_FAIL_COND(_instance_bindings != nullptr && _instance_bindings[0].binding != nullptr);
if (_instance_bindings == nullptr) {
_instance_bindings = (InstanceBinding *)memalloc(sizeof(InstanceBinding));
_instance_binding_count = 1;
}
_instance_bindings[0].binding = p_binding;
_instance_bindings[0].free_callback = p_callbacks->free_callback;
_instance_bindings[0].reference_callback = p_callbacks->reference_callback;
_instance_bindings[0].token = p_token;
_instance_binding_count = 1;
}
void *Object::get_instance_binding(void *p_token, const GDExtensionInstanceBindingCallbacks *p_callbacks) {
@@ -1828,6 +1832,12 @@ void *Object::get_instance_binding(void *p_token, const GDExtensionInstanceBindi
binding = p_callbacks->create_callback(p_token, this);
_instance_bindings[_instance_binding_count].binding = binding;
#ifdef TOOLS_ENABLED
if (!_extension && Engine::get_singleton()->is_extension_reloading_enabled()) {
GDExtensionManager::get_singleton()->track_instance_binding(p_token, this);
}
#endif
_instance_binding_count++;
}
@@ -1851,6 +1861,71 @@ bool Object::has_instance_binding(void *p_token) {
return found;
}
#ifdef TOOLS_ENABLED
void Object::free_instance_binding(void *p_token) {
bool found = false;
_instance_binding_mutex.lock();
for (uint32_t i = 0; i < _instance_binding_count; i++) {
if (!found && _instance_bindings[i].token == p_token) {
if (_instance_bindings[i].free_callback) {
_instance_bindings[i].free_callback(_instance_bindings[i].token, this, _instance_bindings[i].binding);
}
found = true;
}
if (found) {
if (i + 1 < _instance_binding_count) {
_instance_bindings[i] = _instance_bindings[i + 1];
} else {
_instance_bindings[i] = { nullptr };
}
}
}
if (found) {
_instance_binding_count--;
}
_instance_binding_mutex.unlock();
}
void Object::clear_internal_extension() {
ERR_FAIL_NULL(_extension);
// Free the instance inside the GDExtension.
if (_extension->free_instance) {
_extension->free_instance(_extension->class_userdata, _extension_instance);
}
_extension = nullptr;
_extension_instance = nullptr;
// Clear the instance bindings.
_instance_binding_mutex.lock();
if (_instance_bindings[0].free_callback) {
_instance_bindings[0].free_callback(_instance_bindings[0].token, this, _instance_bindings[0].binding);
}
_instance_bindings[0].binding = nullptr;
_instance_bindings[0].token = nullptr;
_instance_bindings[0].free_callback = nullptr;
_instance_bindings[0].reference_callback = nullptr;
_instance_binding_mutex.unlock();
// Clear the virtual methods.
while (virtual_method_list) {
(*virtual_method_list->method) = nullptr;
(*virtual_method_list->initialized) = false;
virtual_method_list = virtual_method_list->next;
}
}
void Object::reset_internal_extension(ObjectGDExtension *p_extension) {
ERR_FAIL_COND(_extension != nullptr);
if (p_extension) {
_extension_instance = p_extension->recreate_instance ? p_extension->recreate_instance(p_extension->class_userdata, (GDExtensionObjectPtr)this) : nullptr;
ERR_FAIL_NULL_MSG(_extension_instance, "Unable to recreate GDExtension instance - does this extension support hot reloading?");
_extension = p_extension;
}
}
#endif
void Object::_construct_object(bool p_reference) {
type_is_reference = p_reference;
_instance_id = ObjectDB::add_instance(this);
@@ -1881,11 +1956,25 @@ Object::~Object() {
}
script_instance = nullptr;
if (_extension && _extension->free_instance) {
_extension->free_instance(_extension->class_userdata, _extension_instance);
if (_extension) {
#ifdef TOOLS_ENABLED
if (_extension->untrack_instance) {
_extension->untrack_instance(_extension->tracking_userdata, this);
}
#endif
if (_extension->free_instance) {
_extension->free_instance(_extension->class_userdata, _extension_instance);
}
_extension = nullptr;
_extension_instance = nullptr;
}
#ifdef TOOLS_ENABLED
else if (_instance_bindings != nullptr && Engine::get_singleton()->is_extension_reloading_enabled()) {
for (uint32_t i = 0; i < _instance_binding_count; i++) {
GDExtensionManager::get_singleton()->untrack_instance_binding(_instance_bindings[i].token, this);
}
}
#endif
if (_emitting) {
//@todo this may need to actually reach the debugger prioritarily somehow because it may crash before