From c9279c1e53402c9c1b06059a80d8ab2c872eb327 Mon Sep 17 00:00:00 2001 From: Lukas Tenbrink Date: Sun, 5 Apr 2026 14:57:59 +0200 Subject: [PATCH] Add `classdb_construct_object3` and `classdb_register_extension_class6`, which are refcount-aware initialization functions (establishing `RefCounted` objects with a refcount of 1). --- core/extension/gdextension.cpp | 24 ++++- core/extension/gdextension.h | 4 +- core/extension/gdextension_interface.cpp | 10 +- core/extension/gdextension_interface.json | 109 ++++++++++++++++++++-- core/object/class_db.cpp | 74 ++++++++++++--- core/object/class_db.h | 4 +- core/object/object.h | 3 +- core/object/ref_counted.cpp | 15 +++ core/object/ref_counted.h | 1 + 9 files changed, 218 insertions(+), 26 deletions(-) diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 7b991b9993f8..62b8e165d331 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -274,6 +274,7 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; nullptr, + false, // p_creation_is_with_refcount }; _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info5, &legacy); } @@ -313,6 +314,7 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtual get_virtual_func; + false, // p_creation_is_with_refcount }; _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info5, &legacy); } @@ -352,6 +354,7 @@ void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_librar p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtual get_virtual_func; + false, // p_creation_is_with_refcount }; _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info5, &legacy); } @@ -366,12 +369,19 @@ void GDExtension::_register_extension_class4(GDExtensionClassLibraryPtr p_librar nullptr, // GDExtensionClassGetRID get_rid; nullptr, // GDExtensionClassGetVirtual get_virtual_func; nullptr, // GDExtensionClassGetVirtual get_virtual_func; + false, // p_creation_is_with_refcount }; _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info5, &legacy); } -#endif // DISABLE_DEPRECATED void GDExtension::_register_extension_class5(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo5 *p_extension_funcs) { + ClassCreationDeprecatedInfo legacy = {}; + legacy.p_creation_is_with_refcount = false; + _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs, &legacy); +} +#endif // DISABLE_DEPRECATED + +void GDExtension::_register_extension_class6(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo6 *p_extension_funcs) { _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs); } @@ -471,7 +481,14 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr extension->gdextension.reference = p_extension_funcs->reference_func; extension->gdextension.unreference = p_extension_funcs->unreference_func; extension->gdextension.class_userdata = p_extension_funcs->class_userdata; - extension->gdextension.create_instance2 = p_extension_funcs->create_instance_func; +#ifndef DISABLE_DEPRECATED + if (p_deprecated_funcs && !p_deprecated_funcs->p_creation_is_with_refcount) { + extension->gdextension.create_instance2 = p_extension_funcs->create_instance_func; + } else +#endif + { + extension->gdextension.create_instance3 = p_extension_funcs->create_instance_func; + } extension->gdextension.free_instance = p_extension_funcs->free_instance_func; extension->gdextension.recreate_instance = p_extension_funcs->recreate_instance_func; extension->gdextension.get_virtual2 = p_extension_funcs->get_virtual_func; @@ -824,8 +841,9 @@ void GDExtension::initialize_gdextensions() { register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2); register_interface_function("classdb_register_extension_class3", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class3); register_interface_function("classdb_register_extension_class4", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class4); -#endif // DISABLE_DEPRECATED register_interface_function("classdb_register_extension_class5", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class5); +#endif // DISABLE_DEPRECATED + register_interface_function("classdb_register_extension_class6", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class6); register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method); register_interface_function("classdb_register_extension_class_virtual_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_virtual_method); register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant); diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index 78b30ac9992c..ae229d8a3ed9 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -72,6 +72,7 @@ class GDExtension : public Resource { GDExtensionClassGetRID get_rid_func = nullptr; GDExtensionClassGetVirtual get_virtual_func = nullptr; GDExtensionClassGetVirtualCallData get_virtual_call_data_func = nullptr; + bool p_creation_is_with_refcount = true; #endif // DISABLE_DEPRECATED }; @@ -80,8 +81,9 @@ class GDExtension : public Resource { static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs); static void _register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs); static void _register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs); -#endif // DISABLE_DEPRECATED static void _register_extension_class5(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo5 *p_extension_funcs); +#endif // DISABLE_DEPRECATED + static void _register_extension_class6(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo6 *p_extension_funcs); static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo5 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr); static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info); static void _register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info); diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index a5bb0dbba0a2..98449c18c612 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -1652,12 +1652,17 @@ static GDExtensionObjectPtr gdextension_classdb_construct_object(GDExtensionCons const StringName classname = *reinterpret_cast(p_classname); return (GDExtensionObjectPtr)ClassDB::instantiate_no_placeholders(classname); } -#endif static GDExtensionObjectPtr gdextension_classdb_construct_object2(GDExtensionConstStringNamePtr p_classname) { const StringName classname = *reinterpret_cast(p_classname); return (GDExtensionObjectPtr)ClassDB::instantiate_without_postinitialization(classname); } +#endif + +static GDExtensionObjectPtr gdextension_classdb_construct_object3(GDExtensionConstStringNamePtr p_classname) { + const StringName classname = *reinterpret_cast(p_classname); + return (GDExtensionObjectPtr)ClassDB::instantiate_without_postinitialization_with_refcount(classname); +} static void *gdextension_classdb_get_class_tag(GDExtensionConstStringNamePtr p_classname) { const StringName classname = *reinterpret_cast(p_classname); @@ -1856,8 +1861,9 @@ void gdextension_setup_interface() { REGISTER_INTERFACE_FUNC(callable_custom_get_userdata); #ifndef DISABLE_DEPRECATED REGISTER_INTERFACE_FUNC(classdb_construct_object); -#endif // DISABLE_DEPRECATED REGISTER_INTERFACE_FUNC(classdb_construct_object2); +#endif // DISABLE_DEPRECATED + REGISTER_INTERFACE_FUNC(classdb_construct_object3); REGISTER_INTERFACE_FUNC(classdb_get_method_bind); REGISTER_INTERFACE_FUNC(classdb_get_class_tag); REGISTER_INTERFACE_FUNC(editor_add_plugin); diff --git a/core/extension/gdextension_interface.json b/core/extension/gdextension_interface.json index b9a6aa56ba4c..6ecd8cb8ddaf 100644 --- a/core/extension/gdextension_interface.json +++ b/core/extension/gdextension_interface.json @@ -1183,6 +1183,23 @@ } ] }, + { + "name": "GDExtensionClassCreateInstance3", + "kind": "function", + "return_value": { + "type": "GDExtensionObjectPtr" + }, + "arguments": [ + { + "name": "p_class_userdata", + "type": "void*" + }, + { + "name": "p_notify_postinitialize", + "type": "GDExtensionBool" + } + ] + }, { "name": "GDExtensionClassFreeInstance", "kind": "function", @@ -1778,6 +1795,11 @@ "kind": "alias", "type": "GDExtensionClassCreationInfo4" }, + { + "name": "GDExtensionClassCreationInfo6", + "kind": "alias", + "type": "GDExtensionClassCreationInfo5" + }, { "name": "GDExtensionClassLibraryPtr", "kind": "handle" @@ -8242,7 +8264,7 @@ "since": "4.1", "deprecated": { "since": "4.4", - "replace_with": "classdb_construct_object2" + "replace_with": "classdb_construct_object3" } }, { @@ -8268,7 +8290,37 @@ "", "\"NOTIFICATION_POSTINITIALIZE\" must be sent after construction." ], - "since": "4.4" + "since": "4.4", + "deprecated": { + "since": "4.6", + "replace_with": "classdb_construct_object3" + } + }, + { + "name": "classdb_construct_object3", + "return_value": { + "type": "GDExtensionObjectPtr", + "description": [ + "A pointer to the newly created Object." + ] + }, + "arguments": [ + { + "name": "p_classname", + "type": "GDExtensionConstStringNamePtr", + "description": [ + "A pointer to a StringName with the class name." + ] + } + ], + "description": [ + "Constructs an Object of the requested class.", + "The passed class must be a built-in godot class, or an already-registered extension class. In both cases, object_set_instance() should be called to fully initialize the object.", + "If the type is a subtype of RefCounted, it already has a refcount of 1. The caller must take ownership the refcount and is responsible for decrementing it again when the object is no longer needed.", + "", + "\"NOTIFICATION_POSTINITIALIZE\" must be sent after construction." + ], + "since": "4.6" }, { "name": "classdb_get_method_bind", @@ -8367,7 +8419,7 @@ "since": "4.1", "deprecated": { "since": "4.2", - "replace_with": "classdb_register_extension_class5" + "replace_with": "classdb_register_extension_class6" } }, { @@ -8409,7 +8461,7 @@ "since": "4.2", "deprecated": { "since": "4.3", - "replace_with": "classdb_register_extension_class5" + "replace_with": "classdb_register_extension_class6" } }, { @@ -8451,7 +8503,7 @@ "since": "4.3", "deprecated": { "since": "4.4", - "replace_with": "classdb_register_extension_class5" + "replace_with": "classdb_register_extension_class6" } }, { @@ -8493,7 +8545,7 @@ "since": "4.4", "deprecated": { "since": "4.5", - "replace_with": "classdb_register_extension_class5" + "replace_with": "classdb_register_extension_class6" } }, { @@ -8532,7 +8584,50 @@ "Registers an extension class in the ClassDB.", "Provided struct can be safely freed once the function returns." ], - "since": "4.5" + "since": "4.5", + "deprecated": { + "since": "4.6", + "replace_with": "classdb_register_extension_class6" + } + }, + { + "name": "classdb_register_extension_class6", + "arguments": [ + { + "name": "p_library", + "type": "GDExtensionClassLibraryPtr", + "description": [ + "A pointer the library received by the GDExtension's entry point function." + ] + }, + { + "name": "p_class_name", + "type": "GDExtensionConstStringNamePtr", + "description": [ + "A pointer to a StringName with the class name." + ] + }, + { + "name": "p_parent_class_name", + "type": "GDExtensionConstStringNamePtr", + "description": [ + "A pointer to a StringName with the parent class name." + ] + }, + { + "name": "p_extension_funcs", + "type": "const GDExtensionClassCreationInfo6*", + "description": [ + "A pointer to a GDExtensionClassCreationInfo6 struct.", + "In contrast to GDExtensionClassCreationInfo5, the creation function must return RefCounted subtypes with a refcount of 1." + ] + } + ], + "description": [ + "Registers an extension class in the ClassDB.", + "Provided struct can be safely freed once the function returns." + ], + "since": "4.6" }, { "name": "classdb_register_extension_class_method", diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 8dec40a6f503..d832cb8f5312 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -532,7 +532,42 @@ StringName ClassDB::get_compatibility_class(const StringName &p_class) { return StringName(); } -Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require_real_class, bool p_notify_postinitialize, bool p_exposed_only) { +Object *ClassDB::_instantiate_from_gdextension(ObjectGDExtension *p_object_gd_extension, bool p_notify_postinitialize, bool p_with_refcount) { + if (p_with_refcount) { + if (p_object_gd_extension->create_instance3 != nullptr) { + // Caller expects refcount=1, creation func returns with refcount=1. It's a match. + return (Object *)p_object_gd_extension->create_instance3(p_object_gd_extension->class_userdata, p_notify_postinitialize); + } +#ifndef DISABLE_DEPRECATED + Object *object = (Object *)p_object_gd_extension->create_instance2(p_object_gd_extension->class_userdata, p_notify_postinitialize); + if (object != nullptr && object->is_ref_counted()) { + // Caller expects a refcount, creation func didn't increment, so do it now. + ((RefCounted *)object)->init_ref(); + } + return object; +#endif + } else { +#ifndef DISABLE_DEPRECATED + if (p_object_gd_extension->create_instance2 != nullptr) { + // Caller expects no refcount, creation func returns without refcount. It's a match. + return (Object *)p_object_gd_extension->create_instance2(p_object_gd_extension->class_userdata, p_notify_postinitialize); + } +#endif + if (p_object_gd_extension->create_instance3 != nullptr) { + Object *object = (Object *)p_object_gd_extension->create_instance3(p_object_gd_extension->class_userdata, p_notify_postinitialize); + if (object != nullptr && object->is_ref_counted()) { + // Caller expects no refcount, but refcount was already incremented by creation func. + // Must fall back to RefCounted's refcount_init trick. + ((RefCounted *)object)->deinit_ref(); + } + return object; + } + } + + ERR_FAIL_V_MSG(nullptr, vformat("Internal error; ObjectGDExtension of %s has neither create_instance2 nor create_instance3.", p_object_gd_extension->class_name)); +} + +Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require_real_class, bool p_notify_postinitialize, bool p_exposed_only, bool p_with_refcount) { ClassInfo *ti; { Locker::Lock lock(Locker::STATE_READ); @@ -568,11 +603,11 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) { bool can_create_placeholder = false; if (ti->gdextension) { - if (ti->gdextension->create_instance2) { + if (ti->gdextension->create_instance3) { can_create_placeholder = true; } #ifndef DISABLE_DEPRECATED - else if (ti->gdextension->create_instance) { + else if (ti->gdextension->create_instance || ti->gdextension->create_instance2) { can_create_placeholder = true; } #endif // DISABLE_DEPRECATED @@ -584,23 +619,32 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require if (can_create_placeholder) { ObjectGDExtension *extension = get_placeholder_extension(ti->gdtype->get_name()); - return (Object *)extension->create_instance2(extension->class_userdata, p_notify_postinitialize); + return _instantiate_from_gdextension(extension, p_notify_postinitialize, p_with_refcount); } } #endif // TOOLS_ENABLED - if (ti->gdextension && ti->gdextension->create_instance2) { + if (ti->gdextension && ti->gdextension->create_instance3) { ObjectGDExtension *extension = ti->gdextension; - return (Object *)extension->create_instance2(extension->class_userdata, p_notify_postinitialize); + return _instantiate_from_gdextension(extension, p_notify_postinitialize, p_with_refcount); } #ifndef DISABLE_DEPRECATED - else if (ti->gdextension && ti->gdextension->create_instance) { + else if (ti->gdextension && ti->gdextension->create_instance2) { + ObjectGDExtension *extension = ti->gdextension; + return _instantiate_from_gdextension(extension, p_notify_postinitialize, p_with_refcount); + } else if (ti->gdextension && ti->gdextension->create_instance) { ObjectGDExtension *extension = ti->gdextension; return (Object *)extension->create_instance(extension->class_userdata); } #endif // DISABLE_DEPRECATED else { - return ti->creation_func(p_notify_postinitialize); + Object *object = ti->creation_func(p_notify_postinitialize); + if (p_with_refcount && object != nullptr && object->is_ref_counted()) { + // creation_func creates the object with an (effective) refcount of 1, + // so establish the expected refcount=1 now. + ((RefCounted *)object)->init_ref(); + } + return object; } } @@ -628,11 +672,15 @@ bool ClassDB::_can_instantiate(ClassInfo *p_class_info, bool p_exposed_only) { return true; } - if (p_class_info->gdextension->create_instance2) { + if (p_class_info->gdextension->create_instance3) { return true; } #ifndef DISABLE_DEPRECATED + if (p_class_info->gdextension->create_instance2) { + return true; + } + if (p_class_info->gdextension->create_instance) { return true; } @@ -652,6 +700,10 @@ Object *ClassDB::instantiate_without_postinitialization(const StringName &p_clas return _instantiate_internal(p_class, true, false); } +Object *ClassDB::instantiate_without_postinitialization_with_refcount(const StringName &p_class) { + return _instantiate_internal(p_class, true, false, true, true); +} + #ifdef TOOLS_ENABLED ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class) { ObjectGDExtension *placeholder_extension = placeholder_extensions.getptr(p_class); @@ -826,9 +878,9 @@ bool ClassDB::is_abstract(const StringName &p_class) { return true; } #ifndef DISABLE_DEPRECATED - return ti->gdextension->create_instance2 == nullptr && ti->gdextension->create_instance == nullptr; + return ti->gdextension->create_instance3 == nullptr && ti->gdextension->create_instance2 == nullptr && ti->gdextension->create_instance2 == nullptr; #else - return ti->gdextension->create_instance2 == nullptr; + return ti->gdextension->create_instance3 == nullptr; #endif // DISABLE_DEPRECATED } diff --git a/core/object/class_db.h b/core/object/class_db.h index b0266f9f5899..6848d46cd0ac 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -234,7 +234,8 @@ class ClassDB { static MethodBind *_bind_vararg_method(MethodBind *p_bind, const StringName &p_name, const Vector &p_default_args, bool p_compatibility); static void _bind_method_custom(const StringName &p_class, MethodBind *p_method, bool p_compatibility); - static Object *_instantiate_internal(const StringName &p_class, bool p_require_real_class = false, bool p_notify_postinitialize = true, bool p_exposed_only = true); + static Object *_instantiate_from_gdextension(ObjectGDExtension *p_object_gd_extension, bool p_notify_postinitialize, bool p_with_refcount); + static Object *_instantiate_internal(const StringName &p_class, bool p_require_real_class = false, bool p_notify_postinitialize = true, bool p_exposed_only = true, bool p_with_refcount = false); static bool _can_instantiate(ClassInfo *p_class_info, bool p_exposed_only = true); @@ -343,6 +344,7 @@ class ClassDB { static Object *instantiate(const StringName &p_class); static Object *instantiate_no_placeholders(const StringName &p_class); static Object *instantiate_without_postinitialization(const StringName &p_class); + static Object *instantiate_without_postinitialization_with_refcount(const StringName &p_class); static void set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance); static APIType get_api_type(const StringName &p_class); diff --git a/core/object/object.h b/core/object/object.h index 0b88aa2f7e7a..4b44cf9059e3 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -110,8 +110,9 @@ struct ObjectGDExtension { #ifndef DISABLE_DEPRECATED GDExtensionClassCreateInstance create_instance; + GDExtensionClassCreateInstance2 create_instance2; // Without refcount. #endif // DISABLE_DEPRECATED - GDExtensionClassCreateInstance2 create_instance2; + GDExtensionClassCreateInstance3 create_instance3; GDExtensionClassFreeInstance free_instance; #ifndef DISABLE_DEPRECATED GDExtensionClassGetVirtual get_virtual; diff --git a/core/object/ref_counted.cpp b/core/object/ref_counted.cpp index 5980adfbec69..d63d8126dcc6 100644 --- a/core/object/ref_counted.cpp +++ b/core/object/ref_counted.cpp @@ -45,6 +45,21 @@ bool RefCounted::init_ref() { } } +void RefCounted::deinit_ref() { + // Somewhat unsafe since we're doing tests in sync, but it's probably fine + // since this function is called from the creation thread, so nobody else should have access. + + // If this succeeds, refcount_init is 2 (or more). + // This would be unexpected, since callers should already have consumed it, or never established it. + // It's a little unsafe to bring it above 1, since it means init_ref must be called more than once, + // but the alternative would be decrementing refcount instead, which is also unsafe since the object + // might unexpectedly destruct early. Better to risk zombying than to risk a crash. + if (!refcount_init.ref()) { + // refcount_init already dead, must re-establish (expected). + refcount_init.init(1); + } +} + void RefCounted::_bind_methods() { ClassDB::bind_method(D_METHOD("init_ref"), &RefCounted::init_ref); ClassDB::bind_method(D_METHOD("reference"), &RefCounted::reference); diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h index 258306b1c732..689a0f267c33 100644 --- a/core/object/ref_counted.h +++ b/core/object/ref_counted.h @@ -46,6 +46,7 @@ class RefCounted : public Object { _FORCE_INLINE_ bool is_referenced() const { return refcount_init.get() != 1; } bool init_ref(); + void deinit_ref(); // Effectively decrements refcount by increasing refcount_init by one. bool reference(); // returns false if refcount is at zero and didn't get increased bool unreference(); int get_reference_count() const;