summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/config/engine.cpp4
-rw-r--r--core/config/engine.h2
-rw-r--r--core/core_bind.cpp19
-rw-r--r--core/core_bind.h1
-rw-r--r--core/crypto/crypto.cpp24
-rw-r--r--core/crypto/crypto.h16
-rw-r--r--core/error/error_list.h1
-rw-r--r--core/extension/gdextension.cpp417
-rw-r--r--core/extension/gdextension.h29
-rw-r--r--core/extension/gdextension_interface.cpp10
-rw-r--r--core/extension/gdextension_interface.h78
-rw-r--r--core/extension/gdextension_library_loader.cpp390
-rw-r--r--core/extension/gdextension_library_loader.h84
-rw-r--r--core/extension/gdextension_loader.h47
-rw-r--r--core/extension/gdextension_manager.cpp19
-rw-r--r--core/extension/gdextension_manager.h1
-rw-r--r--core/io/dtls_server.cpp6
-rw-r--r--core/io/dtls_server.h4
-rw-r--r--core/io/file_access.cpp8
-rw-r--r--core/io/http_client.cpp4
-rw-r--r--core/io/http_client.h4
-rw-r--r--core/io/http_client_tcp.cpp6
-rw-r--r--core/io/http_client_tcp.h2
-rw-r--r--core/io/ip.cpp33
-rw-r--r--core/io/packet_peer_dtls.cpp6
-rw-r--r--core/io/packet_peer_dtls.h4
-rw-r--r--core/io/resource.cpp25
-rw-r--r--core/io/resource.h2
-rw-r--r--core/io/resource_importer.cpp17
-rw-r--r--core/io/resource_importer.h1
-rw-r--r--core/io/resource_loader.cpp333
-rw-r--r--core/io/resource_loader.h16
-rw-r--r--core/io/stream_peer_tls.cpp6
-rw-r--r--core/io/stream_peer_tls.h4
-rw-r--r--core/object/class_db.cpp116
-rw-r--r--core/object/class_db.h21
-rw-r--r--core/object/object.cpp8
-rw-r--r--core/object/object.h4
-rw-r--r--core/object/worker_thread_pool.cpp67
-rw-r--r--core/object/worker_thread_pool.h20
-rw-r--r--core/os/condition_variable.h8
-rw-r--r--core/os/mutex.h24
-rw-r--r--core/os/safe_binary_mutex.h95
-rw-r--r--core/string/node_path.cpp10
-rw-r--r--core/string/ustring.cpp77
-rw-r--r--core/templates/command_queue_mt.cpp8
-rw-r--r--core/templates/command_queue_mt.h18
-rw-r--r--core/templates/paged_allocator.h24
-rw-r--r--core/templates/rid_owner.h40
-rw-r--r--core/templates/sort_array.h6
50 files changed, 1383 insertions, 786 deletions
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 3574430cf7..9cdc21fe8e 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -263,6 +263,10 @@ bool Engine::is_generate_spirv_debug_info_enabled() const {
return generate_spirv_debug_info;
}
+bool Engine::is_extra_gpu_memory_tracking_enabled() const {
+ return extra_gpu_memory_tracking;
+}
+
void Engine::set_print_error_messages(bool p_enabled) {
CoreGlobals::print_error_enabled = p_enabled;
}
diff --git a/core/config/engine.h b/core/config/engine.h
index 7e617d8773..f858eba328 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -72,6 +72,7 @@ private:
bool abort_on_gpu_errors = false;
bool use_validation_layers = false;
bool generate_spirv_debug_info = false;
+ bool extra_gpu_memory_tracking = false;
int32_t gpu_idx = -1;
uint64_t _process_frames = 0;
@@ -181,6 +182,7 @@ public:
bool is_abort_on_gpu_errors_enabled() const;
bool is_validation_layers_enabled() const;
bool is_generate_spirv_debug_info_enabled() const;
+ bool is_extra_gpu_memory_tracking_enabled() const;
int32_t get_gpu_index() const;
void increment_frames_drawn();
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 36f662b92b..84f66c3479 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -1501,6 +1501,23 @@ TypedArray<Dictionary> ClassDB::class_get_method_list(const StringName &p_class,
return ret;
}
+Variant ClassDB::class_call_static_method(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) {
+ if (p_argcount < 2) {
+ r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ return Variant::NIL;
+ }
+ if (!p_arguments[0]->is_string() || !p_arguments[1]->is_string()) {
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ return Variant::NIL;
+ }
+ StringName class_ = *p_arguments[0];
+ StringName method = *p_arguments[1];
+ const MethodBind *bind = ::ClassDB::get_method(class_, method);
+ ERR_FAIL_NULL_V_MSG(bind, Variant::NIL, "Cannot find static method.");
+ ERR_FAIL_COND_V_MSG(!bind->is_static(), Variant::NIL, "Method is not static.");
+ return bind->call(nullptr, p_arguments + 2, p_argcount - 2, r_call_error);
+}
+
PackedStringArray ClassDB::class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance) const {
List<String> constants;
::ClassDB::get_integer_constant_list(p_class, &constants, p_no_inheritance);
@@ -1623,6 +1640,8 @@ void ClassDB::_bind_methods() {
::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::class_get_method_list, DEFVAL(false));
+ ::ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "class_call_static_method", &ClassDB::class_call_static_method, MethodInfo("class_call_static_method", PropertyInfo(Variant::STRING_NAME, "class"), PropertyInfo(Variant::STRING_NAME, "method")));
+
::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::class_get_integer_constant_list, DEFVAL(false));
::ClassDB::bind_method(D_METHOD("class_has_integer_constant", "class", "name"), &ClassDB::class_has_integer_constant);
diff --git a/core/core_bind.h b/core/core_bind.h
index d744da2551..0949ba628f 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -460,6 +460,7 @@ public:
int class_get_method_argument_count(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const;
TypedArray<Dictionary> class_get_method_list(const StringName &p_class, bool p_no_inheritance = false) const;
+ Variant class_call_static_method(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error);
PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const;
bool class_has_integer_constant(const StringName &p_class, const StringName &p_name) const;
diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp
index d3d0079410..62bacadf91 100644
--- a/core/crypto/crypto.cpp
+++ b/core/crypto/crypto.cpp
@@ -36,10 +36,10 @@
/// Resources
-CryptoKey *(*CryptoKey::_create)() = nullptr;
-CryptoKey *CryptoKey::create() {
+CryptoKey *(*CryptoKey::_create)(bool p_notify_postinitialize) = nullptr;
+CryptoKey *CryptoKey::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
return nullptr;
}
@@ -52,10 +52,10 @@ void CryptoKey::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_from_string", "string_key", "public_only"), &CryptoKey::load_from_string, DEFVAL(false));
}
-X509Certificate *(*X509Certificate::_create)() = nullptr;
-X509Certificate *X509Certificate::create() {
+X509Certificate *(*X509Certificate::_create)(bool p_notify_postinitialize) = nullptr;
+X509Certificate *X509Certificate::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
return nullptr;
}
@@ -116,10 +116,10 @@ void HMACContext::_bind_methods() {
ClassDB::bind_method(D_METHOD("finish"), &HMACContext::finish);
}
-HMACContext *(*HMACContext::_create)() = nullptr;
-HMACContext *HMACContext::create() {
+HMACContext *(*HMACContext::_create)(bool p_notify_postinitialize) = nullptr;
+HMACContext *HMACContext::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
ERR_FAIL_V_MSG(nullptr, "HMACContext is not available when the mbedtls module is disabled.");
}
@@ -127,10 +127,10 @@ HMACContext *HMACContext::create() {
/// Crypto
void (*Crypto::_load_default_certificates)(const String &p_path) = nullptr;
-Crypto *(*Crypto::_create)() = nullptr;
-Crypto *Crypto::create() {
+Crypto *(*Crypto::_create)(bool p_notify_postinitialize) = nullptr;
+Crypto *Crypto::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
ERR_FAIL_V_MSG(nullptr, "Crypto is not available when the mbedtls module is disabled.");
}
diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h
index 16649422cf..c19e6b6773 100644
--- a/core/crypto/crypto.h
+++ b/core/crypto/crypto.h
@@ -42,10 +42,10 @@ class CryptoKey : public Resource {
protected:
static void _bind_methods();
- static CryptoKey *(*_create)();
+ static CryptoKey *(*_create)(bool p_notify_postinitialize);
public:
- static CryptoKey *create();
+ static CryptoKey *create(bool p_notify_postinitialize = true);
virtual Error load(const String &p_path, bool p_public_only = false) = 0;
virtual Error save(const String &p_path, bool p_public_only = false) = 0;
virtual String save_to_string(bool p_public_only = false) = 0;
@@ -58,10 +58,10 @@ class X509Certificate : public Resource {
protected:
static void _bind_methods();
- static X509Certificate *(*_create)();
+ static X509Certificate *(*_create)(bool p_notify_postinitialize);
public:
- static X509Certificate *create();
+ static X509Certificate *create(bool p_notify_postinitialize = true);
virtual Error load(const String &p_path) = 0;
virtual Error load_from_memory(const uint8_t *p_buffer, int p_len) = 0;
virtual Error save(const String &p_path) = 0;
@@ -106,10 +106,10 @@ class HMACContext : public RefCounted {
protected:
static void _bind_methods();
- static HMACContext *(*_create)();
+ static HMACContext *(*_create)(bool p_notify_postinitialize);
public:
- static HMACContext *create();
+ static HMACContext *create(bool p_notify_postinitialize = true);
virtual Error start(HashingContext::HashType p_hash_type, const PackedByteArray &p_key) = 0;
virtual Error update(const PackedByteArray &p_data) = 0;
@@ -124,11 +124,11 @@ class Crypto : public RefCounted {
protected:
static void _bind_methods();
- static Crypto *(*_create)();
+ static Crypto *(*_create)(bool p_notify_postinitialize);
static void (*_load_default_certificates)(const String &p_path);
public:
- static Crypto *create();
+ static Crypto *create(bool p_notify_postinitialize = true);
static void load_default_certificates(const String &p_path);
virtual PackedByteArray generate_random_bytes(int p_bytes) = 0;
diff --git a/core/error/error_list.h b/core/error/error_list.h
index abc637106a..cdf06eb06d 100644
--- a/core/error/error_list.h
+++ b/core/error/error_list.h
@@ -41,6 +41,7 @@
* - Are added to the Error enum in core/error/error_list.h
* - Have a description added to error_names in core/error/error_list.cpp
* - Are bound with BIND_CORE_ENUM_CONSTANT() in core/core_constants.cpp
+ * - Have a matching Android version in platform/android/java/lib/src/org/godotengine/godot/error/Error.kt
*/
enum Error {
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index cb6832ea39..940a34396f 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -32,11 +32,9 @@
#include "gdextension.compat.inc"
#include "core/config/project_settings.h"
-#include "core/io/dir_access.h"
#include "core/object/class_db.h"
#include "core/object/method_bind.h"
-#include "core/os/os.h"
-#include "core/version.h"
+#include "gdextension_library_loader.h"
#include "gdextension_manager.h"
extern void gdextension_setup_interface();
@@ -48,146 +46,6 @@ String GDExtension::get_extension_list_config_file() {
return ProjectSettings::get_singleton()->get_project_data_path().path_join("extension_list.cfg");
}
-Vector<SharedObject> GDExtension::find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature) {
- Vector<SharedObject> dependencies_shared_objects;
- if (p_config->has_section("dependencies")) {
- List<String> config_dependencies;
- p_config->get_section_keys("dependencies", &config_dependencies);
-
- for (const String &dependency : config_dependencies) {
- Vector<String> dependency_tags = dependency.split(".");
- bool all_tags_met = true;
- for (int i = 0; i < dependency_tags.size(); i++) {
- String tag = dependency_tags[i].strip_edges();
- if (!p_has_feature(tag)) {
- all_tags_met = false;
- break;
- }
- }
-
- if (all_tags_met) {
- Dictionary dependency_value = p_config->get_value("dependencies", dependency);
- for (const Variant *key = dependency_value.next(nullptr); key; key = dependency_value.next(key)) {
- String dependency_path = *key;
- String target_path = dependency_value[*key];
- if (dependency_path.is_relative_path()) {
- dependency_path = p_path.get_base_dir().path_join(dependency_path);
- }
- dependencies_shared_objects.push_back(SharedObject(dependency_path, dependency_tags, target_path));
- }
- break;
- }
- }
- }
-
- return dependencies_shared_objects;
-}
-
-String GDExtension::find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags) {
- // First, check the explicit libraries.
- if (p_config->has_section("libraries")) {
- List<String> libraries;
- p_config->get_section_keys("libraries", &libraries);
-
- // Iterate the libraries, finding the best matching tags.
- String best_library_path;
- Vector<String> best_library_tags;
- for (const String &E : libraries) {
- Vector<String> tags = E.split(".");
- bool all_tags_met = true;
- for (int i = 0; i < tags.size(); i++) {
- String tag = tags[i].strip_edges();
- if (!p_has_feature(tag)) {
- all_tags_met = false;
- break;
- }
- }
-
- if (all_tags_met && tags.size() > best_library_tags.size()) {
- best_library_path = p_config->get_value("libraries", E);
- best_library_tags = tags;
- }
- }
-
- if (!best_library_path.is_empty()) {
- if (best_library_path.is_relative_path()) {
- best_library_path = p_path.get_base_dir().path_join(best_library_path);
- }
- if (r_tags != nullptr) {
- r_tags->append_array(best_library_tags);
- }
- return best_library_path;
- }
- }
-
- // Second, try to autodetect
- String autodetect_library_prefix;
- if (p_config->has_section_key("configuration", "autodetect_library_prefix")) {
- autodetect_library_prefix = p_config->get_value("configuration", "autodetect_library_prefix");
- }
- if (!autodetect_library_prefix.is_empty()) {
- String autodetect_path = autodetect_library_prefix;
- if (autodetect_path.is_relative_path()) {
- autodetect_path = p_path.get_base_dir().path_join(autodetect_path);
- }
-
- // Find the folder and file parts of the prefix.
- String folder;
- String file_prefix;
- if (DirAccess::dir_exists_absolute(autodetect_path)) {
- folder = autodetect_path;
- } else if (DirAccess::dir_exists_absolute(autodetect_path.get_base_dir())) {
- folder = autodetect_path.get_base_dir();
- file_prefix = autodetect_path.get_file();
- } else {
- ERR_FAIL_V_MSG(String(), vformat("Error in extension: %s. Could not find folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
- }
-
- // Open the folder.
- Ref<DirAccess> dir = DirAccess::open(folder);
- ERR_FAIL_COND_V_MSG(!dir.is_valid(), String(), vformat("Error in extension: %s. Could not open folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
-
- // Iterate the files and check the prefixes, finding the best matching file.
- String best_file;
- Vector<String> best_file_tags;
- dir->list_dir_begin();
- String file_name = dir->_get_next();
- while (file_name != "") {
- if (!dir->current_is_dir() && file_name.begins_with(file_prefix)) {
- // Check if the files matches all requested feature tags.
- String tags_str = file_name.trim_prefix(file_prefix);
- tags_str = tags_str.trim_suffix(tags_str.get_extension());
-
- Vector<String> tags = tags_str.split(".", false);
- bool all_tags_met = true;
- for (int i = 0; i < tags.size(); i++) {
- String tag = tags[i].strip_edges();
- if (!p_has_feature(tag)) {
- all_tags_met = false;
- break;
- }
- }
-
- // If all tags are found in the feature list, and we found more tags than before, use this file.
- if (all_tags_met && tags.size() > best_file_tags.size()) {
- best_file_tags = tags;
- best_file = file_name;
- }
- }
- file_name = dir->_get_next();
- }
-
- if (!best_file.is_empty()) {
- String library_path = folder.path_join(best_file);
- if (r_tags != nullptr) {
- r_tags->append_array(best_file_tags);
- }
- return library_path;
- }
- }
- return String();
-}
-
class GDExtensionMethodBind : public MethodBind {
GDExtensionClassMethodCall call_func;
GDExtensionClassMethodValidatedCall validated_call_func;
@@ -382,7 +240,7 @@ public:
#ifndef DISABLE_DEPRECATED
void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) {
- const GDExtensionClassCreationInfo3 class_info3 = {
+ const GDExtensionClassCreationInfo4 class_info4 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
true, // GDExtensionBool is_exposed;
@@ -398,7 +256,7 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
- p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
+ nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */
p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
nullptr, // GDExtensionClassRecreateInstance recreate_instance_func;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
@@ -411,12 +269,13 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
const ClassCreationDeprecatedInfo legacy = {
p_extension_funcs->notification_func, // GDExtensionClassNotification notification_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
};
- _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
+ _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy);
}
void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs) {
- const GDExtensionClassCreationInfo3 class_info3 = {
+ const GDExtensionClassCreationInfo4 class_info4 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
p_extension_funcs->is_exposed, // GDExtensionBool is_exposed;
@@ -432,7 +291,7 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar
p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
- p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
+ nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */
p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
@@ -445,16 +304,53 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar
const ClassCreationDeprecatedInfo legacy = {
nullptr, // GDExtensionClassNotification notification_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
};
- _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
+ _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy);
}
-#endif // DISABLE_DEPRECATED
void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs) {
+ const GDExtensionClassCreationInfo4 class_info4 = {
+ p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
+ p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
+ p_extension_funcs->is_exposed, // GDExtensionBool is_exposed;
+ p_extension_funcs->is_runtime, // GDExtensionBool is_runtime;
+ p_extension_funcs->set_func, // GDExtensionClassSet set_func;
+ p_extension_funcs->get_func, // GDExtensionClassGet get_func;
+ p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
+ p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
+ p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
+ p_extension_funcs->validate_property_func, // GDExtensionClassValidateProperty validate_property_func;
+ p_extension_funcs->notification_func, // GDExtensionClassNotification2 notification_func;
+ p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
+ p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
+ p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
+ nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */
+ p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
+ p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
+ p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
+ p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
+ p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func;
+ p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
+ p_extension_funcs->class_userdata, // void *class_userdata;
+ };
+
+ const ClassCreationDeprecatedInfo legacy = {
+ nullptr, // GDExtensionClassNotification notification_func;
+ nullptr, // GDExtensionClassFreePropertyList free_property_list_func;
+ p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance2 create_instance_func;
+ };
+ _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy);
+}
+
+#endif // DISABLE_DEPRECATED
+
+void GDExtension::_register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs) {
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs);
}
-void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) {
+void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) {
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
@@ -530,6 +426,7 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
if (p_deprecated_funcs) {
extension->gdextension.notification = p_deprecated_funcs->notification_func;
extension->gdextension.free_property_list = p_deprecated_funcs->free_property_list_func;
+ extension->gdextension.create_instance = p_deprecated_funcs->create_instance_func;
}
#endif // DISABLE_DEPRECATED
extension->gdextension.notification2 = p_extension_funcs->notification_func;
@@ -537,7 +434,7 @@ 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_instance = p_extension_funcs->create_instance_func;
+ extension->gdextension.create_instance2 = 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_virtual = p_extension_funcs->get_virtual_func;
@@ -755,7 +652,13 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringPtr r_path) {
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
- memnew_placement(r_path, String(self->library_path));
+ Ref<GDExtensionLibraryLoader> library_loader = self->loader;
+ String library_path;
+ if (library_loader.is_valid()) {
+ library_path = library_loader->library_path;
+ }
+
+ memnew_placement(r_path, String(library_path));
}
HashMap<StringName, GDExtensionInterfaceFunctionPtr> GDExtension::gdextension_interface_functions;
@@ -771,55 +674,34 @@ GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(const String
return *function;
}
-Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol, Vector<SharedObject> *p_dependencies) {
+Error GDExtension::open_library(const String &p_path, const Ref<GDExtensionLoader> &p_loader) {
+ ERR_FAIL_NULL_V_MSG(p_loader, FAILED, "Can't open GDExtension without a loader.");
+ loader = p_loader;
+
String abs_path = ProjectSettings::get_singleton()->globalize_path(p_path);
- Vector<String> abs_dependencies_paths;
- if (p_dependencies != nullptr && !p_dependencies->is_empty()) {
- for (const SharedObject &dependency : *p_dependencies) {
- abs_dependencies_paths.push_back(ProjectSettings::get_singleton()->globalize_path(dependency.path));
- }
- }
-
- OS::GDExtensionData data = {
- true, // also_set_library_path
- &library_path, // r_resolved_path
- Engine::get_singleton()->is_editor_hint(), // generate_temp_files
- &abs_dependencies_paths, // library_dependencies
- };
- Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, &data);
+ Error err = loader->open_library(abs_path);
ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path);
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path);
- void *entry_funcptr = nullptr;
-
- err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, p_entry_symbol, entry_funcptr, false);
+ err = loader->initialize(&gdextension_get_proc_address, this, &initialization);
if (err != OK) {
- ERR_PRINT("GDExtension entry point '" + p_entry_symbol + "' not found in library " + abs_path);
- OS::get_singleton()->close_dynamic_library(library);
+ // Errors already logged in initialize().
+ loader->close_library();
return err;
}
- GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
- GDExtensionBool ret = initialization_function(&gdextension_get_proc_address, this, &initialization);
+ level_initialized = -1;
- if (ret) {
- level_initialized = -1;
- return OK;
- } else {
- ERR_PRINT("GDExtension initialization function '" + p_entry_symbol + "' returned an error.");
- OS::get_singleton()->close_dynamic_library(library);
- return FAILED;
- }
+ return OK;
}
void GDExtension::close_library() {
- ERR_FAIL_NULL(library);
- OS::get_singleton()->close_dynamic_library(library);
+ ERR_FAIL_COND(!is_library_open());
+ loader->close_library();
- library = nullptr;
class_icon_paths.clear();
#ifdef TOOLS_ENABLED
@@ -828,16 +710,16 @@ void GDExtension::close_library() {
}
bool GDExtension::is_library_open() const {
- return library != nullptr;
+ return loader.is_valid() && loader->is_library_open();
}
GDExtension::InitializationLevel GDExtension::get_minimum_library_initialization_level() const {
- ERR_FAIL_NULL_V(library, INITIALIZATION_LEVEL_CORE);
+ ERR_FAIL_COND_V(!is_library_open(), INITIALIZATION_LEVEL_CORE);
return InitializationLevel(initialization.minimum_initialization_level);
}
void GDExtension::initialize_library(InitializationLevel p_level) {
- ERR_FAIL_NULL(library);
+ ERR_FAIL_COND(!is_library_open());
ERR_FAIL_COND_MSG(p_level <= int32_t(level_initialized), vformat("Level '%d' must be higher than the current level '%d'", p_level, level_initialized));
level_initialized = int32_t(p_level);
@@ -847,7 +729,7 @@ void GDExtension::initialize_library(InitializationLevel p_level) {
initialization.initialize(initialization.userdata, GDExtensionInitializationLevel(p_level));
}
void GDExtension::deinitialize_library(InitializationLevel p_level) {
- ERR_FAIL_NULL(library);
+ ERR_FAIL_COND(!is_library_open());
ERR_FAIL_COND(p_level > int32_t(level_initialized));
level_initialized = int32_t(p_level) - 1;
@@ -871,7 +753,7 @@ GDExtension::GDExtension() {
}
GDExtension::~GDExtension() {
- if (library != nullptr) {
+ if (is_library_open()) {
close_library();
}
#ifdef TOOLS_ENABLED
@@ -888,8 +770,9 @@ void GDExtension::initialize_gdextensions() {
#ifndef DISABLE_DEPRECATED
register_interface_function("classdb_register_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class);
register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2);
-#endif // DISABLE_DEPRECATED
register_interface_function("classdb_register_extension_class3", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class3);
+#endif // DISABLE_DEPRECATED
+ register_interface_function("classdb_register_extension_class4", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class4);
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);
@@ -909,142 +792,15 @@ void GDExtension::finalize_gdextensions() {
Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension) {
ERR_FAIL_COND_V_MSG(p_extension.is_valid() && p_extension->is_library_open(), ERR_ALREADY_IN_USE, "Cannot load GDExtension resource into already opened library.");
- Ref<ConfigFile> config;
- config.instantiate();
-
- Error err = config->load(p_path);
-
- if (err != OK) {
- ERR_PRINT("Error loading GDExtension configuration file: " + p_path);
- return err;
- }
+ GDExtensionManager *extension_manager = GDExtensionManager::get_singleton();
- if (!config->has_section_key("configuration", "entry_symbol")) {
- ERR_PRINT("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: " + p_path);
- return ERR_INVALID_DATA;
- }
-
- String entry_symbol = config->get_value("configuration", "entry_symbol");
-
- uint32_t compatibility_minimum[3] = { 0, 0, 0 };
- if (config->has_section_key("configuration", "compatibility_minimum")) {
- String compat_string = config->get_value("configuration", "compatibility_minimum");
- Vector<int> parts = compat_string.split_ints(".");
- for (int i = 0; i < parts.size(); i++) {
- if (i >= 3) {
- break;
- }
- if (parts[i] >= 0) {
- compatibility_minimum[i] = parts[i];
- }
- }
- } else {
- ERR_PRINT("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: " + p_path);
- return ERR_INVALID_DATA;
- }
-
- if (compatibility_minimum[0] < 4 || (compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0)) {
- ERR_PRINT(vformat("GDExtension's compatibility_minimum (%d.%d.%d) must be at least 4.1.0: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
- return ERR_INVALID_DATA;
- }
-
- bool compatible = true;
- // Check version lexicographically.
- if (VERSION_MAJOR != compatibility_minimum[0]) {
- compatible = VERSION_MAJOR > compatibility_minimum[0];
- } else if (VERSION_MINOR != compatibility_minimum[1]) {
- compatible = VERSION_MINOR > compatibility_minimum[1];
- } else {
- compatible = VERSION_PATCH >= compatibility_minimum[2];
- }
- if (!compatible) {
- ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
- return ERR_INVALID_DATA;
- }
-
- // Optionally check maximum compatibility.
- if (config->has_section_key("configuration", "compatibility_maximum")) {
- uint32_t compatibility_maximum[3] = { 0, 0, 0 };
- String compat_string = config->get_value("configuration", "compatibility_maximum");
- Vector<int> parts = compat_string.split_ints(".");
- for (int i = 0; i < 3; i++) {
- if (i < parts.size() && parts[i] >= 0) {
- compatibility_maximum[i] = parts[i];
- } else {
- // If a version part is missing, set the maximum to an arbitrary high value.
- compatibility_maximum[i] = 9999;
- }
- }
-
- compatible = true;
- if (VERSION_MAJOR != compatibility_maximum[0]) {
- compatible = VERSION_MAJOR < compatibility_maximum[0];
- } else if (VERSION_MINOR != compatibility_maximum[1]) {
- compatible = VERSION_MINOR < compatibility_maximum[1];
- }
-#if VERSION_PATCH
- // #if check to avoid -Wtype-limits warning when 0.
- else {
- compatible = VERSION_PATCH <= compatibility_maximum[2];
- }
-#endif
-
- if (!compatible) {
- ERR_PRINT(vformat("GDExtension only compatible with Godot version %s or earlier: %s", compat_string, p_path));
- return ERR_INVALID_DATA;
- }
- }
-
- String library_path = GDExtension::find_extension_library(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
-
- if (library_path.is_empty()) {
- const String os_arch = OS::get_singleton()->get_name().to_lower() + "." + Engine::get_singleton()->get_architecture_name();
- ERR_PRINT(vformat("No GDExtension library found for current OS and architecture (%s) in configuration file: %s", os_arch, p_path));
- return ERR_FILE_NOT_FOUND;
- }
-
- bool is_static_library = library_path.ends_with(".a") || library_path.ends_with(".xcframework");
-
- if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
- library_path = p_path.get_base_dir().path_join(library_path);
- }
-
- if (p_extension.is_null()) {
- p_extension.instantiate();
- }
-
-#ifdef TOOLS_ENABLED
- p_extension->set_reloadable(config->get_value("configuration", "reloadable", false) && Engine::get_singleton()->is_extension_reloading_enabled());
-
- p_extension->update_last_modified_time(
- FileAccess::get_modified_time(p_path),
- FileAccess::get_modified_time(library_path));
-#endif
-
- Vector<SharedObject> library_dependencies = GDExtension::find_extension_dependencies(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
- err = p_extension->open_library(is_static_library ? String() : library_path, entry_symbol, &library_dependencies);
- if (err != OK) {
- // Unreference the extension so that this loading can be considered a failure.
- p_extension.unref();
-
- // Errors already logged in open_library()
- return err;
- }
-
- // Handle icons if any are specified.
- if (config->has_section("icons")) {
- List<String> keys;
- config->get_section_keys("icons", &keys);
- for (const String &key : keys) {
- String icon_path = config->get_value("icons", key);
- if (icon_path.is_relative_path()) {
- icon_path = p_path.get_base_dir().path_join(icon_path);
- }
-
- p_extension->class_icon_paths[key] = icon_path;
- }
+ GDExtensionManager::LoadStatus status = extension_manager->load_extension(p_path);
+ if (status != GDExtensionManager::LOAD_STATUS_OK && status != GDExtensionManager::LOAD_STATUS_ALREADY_LOADED) {
+ // Errors already logged in load_extension().
+ return FAILED;
}
+ p_extension = extension_manager->get_extension(p_path);
return OK;
}
@@ -1085,16 +841,7 @@ String GDExtensionResourceLoader::get_resource_type(const String &p_path) const
#ifdef TOOLS_ENABLED
bool GDExtension::has_library_changed() const {
- // Check only that the last modified time is different (rather than checking
- // that it's newer) since some OS's (namely Windows) will preserve the modified
- // time by default when copying files.
- if (FileAccess::get_modified_time(get_path()) != resource_last_modified_time) {
- return true;
- }
- if (FileAccess::get_modified_time(library_path) != library_last_modified_time) {
- return true;
- }
- return false;
+ return loader->has_library_changed();
}
void GDExtension::prepare_reload() {
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 9393e7399b..7bb4294909 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -31,13 +31,11 @@
#ifndef GDEXTENSION_H
#define GDEXTENSION_H
-#include <functional>
-
#include "core/extension/gdextension_interface.h"
+#include "core/extension/gdextension_loader.h"
#include "core/io/config_file.h"
#include "core/io/resource_loader.h"
#include "core/object/ref_counted.h"
-#include "core/os/shared_object.h"
class GDExtensionMethodBind;
@@ -46,8 +44,8 @@ class GDExtension : public Resource {
friend class GDExtensionManager;
- void *library = nullptr; // pointer if valid,
- String library_path;
+ Ref<GDExtensionLoader> loader;
+
bool reloadable = false;
struct Extension {
@@ -72,15 +70,17 @@ class GDExtension : public Resource {
#ifndef DISABLE_DEPRECATED
GDExtensionClassNotification notification_func = nullptr;
GDExtensionClassFreePropertyList free_property_list_func = nullptr;
+ GDExtensionClassCreateInstance create_instance_func = nullptr;
#endif // DISABLE_DEPRECATED
};
#ifndef DISABLE_DEPRECATED
static void _register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs);
static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs);
-#endif // DISABLE_DEPRECATED
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_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr);
+#endif // DISABLE_DEPRECATED
+ static void _register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs);
+ static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *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);
static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield);
@@ -96,8 +96,6 @@ class GDExtension : public Resource {
int32_t level_initialized = -1;
#ifdef TOOLS_ENABLED
- uint64_t resource_last_modified_time = 0;
- uint64_t library_last_modified_time = 0;
bool is_reloading = false;
Vector<GDExtensionMethodBind *> invalid_methods;
Vector<ObjectID> instance_bindings;
@@ -124,11 +122,12 @@ public:
virtual bool editor_can_reload_from_file() override { return false; } // Reloading is handled in a special way.
static String get_extension_list_config_file();
- static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);
- static Vector<SharedObject> find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature);
- Error open_library(const String &p_path, const String &p_entry_symbol, Vector<SharedObject> *p_dependencies = nullptr);
+ const Ref<GDExtensionLoader> get_loader() const { return loader; }
+
+ Error open_library(const String &p_path, const Ref<GDExtensionLoader> &p_loader);
void close_library();
+ bool is_library_open() const;
enum InitializationLevel {
INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
@@ -146,17 +145,11 @@ protected:
#endif
public:
- bool is_library_open() const;
-
#ifdef TOOLS_ENABLED
bool is_reloadable() const { return reloadable; }
void set_reloadable(bool p_reloadable) { reloadable = p_reloadable; }
bool has_library_changed() const;
- void update_last_modified_time(uint64_t p_resource_last_modified_time, uint64_t p_library_last_modified_time) {
- resource_last_modified_time = p_resource_last_modified_time;
- library_last_modified_time = p_library_last_modified_time;
- }
void track_instance_binding(Object *p_object);
void untrack_instance_binding(Object *p_object);
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 85f83eecfd..a5a0fc906a 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -1515,10 +1515,17 @@ static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionC
return (GDExtensionMethodBindPtr)mb;
}
+#ifndef DISABLE_DEPRECATED
static GDExtensionObjectPtr gdextension_classdb_construct_object(GDExtensionConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
return (GDExtensionObjectPtr)ClassDB::instantiate_no_placeholders(classname);
}
+#endif
+
+static GDExtensionObjectPtr gdextension_classdb_construct_object2(GDExtensionConstStringNamePtr p_classname) {
+ const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
+ return (GDExtensionObjectPtr)ClassDB::instantiate_without_postinitialization(classname);
+}
static void *gdextension_classdb_get_class_tag(GDExtensionConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
@@ -1701,7 +1708,10 @@ void gdextension_setup_interface() {
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(callable_custom_create2);
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);
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.h b/core/extension/gdextension_interface.h
index fce377f967..cac76d39bd 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -268,6 +268,7 @@ typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance
typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);
typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_class_userdata);
+typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance2)(void *p_class_userdata, bool p_notify_postinitialize);
typedef void (*GDExtensionClassFreeInstance)(void *p_class_userdata, GDExtensionClassInstancePtr p_instance);
typedef GDExtensionClassInstancePtr (*GDExtensionClassRecreateInstance)(void *p_class_userdata, GDExtensionObjectPtr p_object);
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
@@ -292,7 +293,7 @@ typedef struct {
GDExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function.
GDExtensionClassGetRID get_rid_func;
void *class_userdata; // Per-class user data, later accessible in instance bindings.
-} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo3 instead.
+} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo4 instead.
typedef struct {
GDExtensionBool is_virtual;
@@ -325,7 +326,7 @@ typedef struct {
GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
GDExtensionClassGetRID get_rid_func;
void *class_userdata; // Per-class user data, later accessible in instance bindings.
-} GDExtensionClassCreationInfo2; // Deprecated. Use GDExtensionClassCreationInfo3 instead.
+} GDExtensionClassCreationInfo2; // Deprecated. Use GDExtensionClassCreationInfo4 instead.
typedef struct {
GDExtensionBool is_virtual;
@@ -359,7 +360,41 @@ typedef struct {
GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
GDExtensionClassGetRID get_rid_func;
void *class_userdata; // Per-class user data, later accessible in instance bindings.
-} GDExtensionClassCreationInfo3;
+} GDExtensionClassCreationInfo3; // Deprecated. Use GDExtensionClassCreationInfo4 instead.
+
+typedef struct {
+ GDExtensionBool is_virtual;
+ GDExtensionBool is_abstract;
+ GDExtensionBool is_exposed;
+ GDExtensionBool is_runtime;
+ GDExtensionClassSet set_func;
+ GDExtensionClassGet get_func;
+ GDExtensionClassGetPropertyList get_property_list_func;
+ GDExtensionClassFreePropertyList2 free_property_list_func;
+ GDExtensionClassPropertyCanRevert property_can_revert_func;
+ GDExtensionClassPropertyGetRevert property_get_revert_func;
+ GDExtensionClassValidateProperty validate_property_func;
+ GDExtensionClassNotification2 notification_func;
+ GDExtensionClassToString to_string_func;
+ GDExtensionClassReference reference_func;
+ GDExtensionClassUnreference unreference_func;
+ GDExtensionClassCreateInstance2 create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract.
+ GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory.
+ GDExtensionClassRecreateInstance recreate_instance_func;
+ // Queries a virtual function by name and returns a callback to invoke the requested virtual function.
+ GDExtensionClassGetVirtual get_virtual_func;
+ // Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that
+ // need or benefit from extra data when calling virtual functions.
+ // Returns user data that will be passed to `call_virtual_with_data_func`.
+ // Returning `NULL` from this function signals to Godot that the virtual function is not overridden.
+ // Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized.
+ // You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`.
+ GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
+ // Used to call virtual functions when `get_virtual_call_data_func` is not null.
+ GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
+ GDExtensionClassGetRID get_rid_func;
+ void *class_userdata; // Per-class user data, later accessible in instance bindings.
+} GDExtensionClassCreationInfo4;
typedef void *GDExtensionClassLibraryPtr;
@@ -2680,6 +2715,7 @@ typedef void *(*GDExtensionInterfaceCallableCustomGetUserData)(GDExtensionConstT
/**
* @name classdb_construct_object
* @since 4.1
+ * @deprecated in Godot 4.4. Use `classdb_construct_object2` instead.
*
* Constructs an Object of the requested class.
*
@@ -2692,6 +2728,22 @@ typedef void *(*GDExtensionInterfaceCallableCustomGetUserData)(GDExtensionConstT
typedef GDExtensionObjectPtr (*GDExtensionInterfaceClassdbConstructObject)(GDExtensionConstStringNamePtr p_classname);
/**
+ * @name classdb_construct_object2
+ * @since 4.4
+ *
+ * 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.
+ *
+ * "NOTIFICATION_POSTINITIALIZE" must be sent after construction.
+ *
+ * @param p_classname A pointer to a StringName with the class name.
+ *
+ * @return A pointer to the newly created Object.
+ */
+typedef GDExtensionObjectPtr (*GDExtensionInterfaceClassdbConstructObject2)(GDExtensionConstStringNamePtr p_classname);
+
+/**
* @name classdb_get_method_bind
* @since 4.1
*
@@ -2722,7 +2774,7 @@ typedef void *(*GDExtensionInterfaceClassdbGetClassTag)(GDExtensionConstStringNa
/**
* @name classdb_register_extension_class
* @since 4.1
- * @deprecated in Godot 4.2. Use `classdb_register_extension_class3` instead.
+ * @deprecated in Godot 4.2. Use `classdb_register_extension_class4` instead.
*
* Registers an extension class in the ClassDB.
*
@@ -2738,7 +2790,7 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionCla
/**
* @name classdb_register_extension_class2
* @since 4.2
- * @deprecated in Godot 4.3. Use `classdb_register_extension_class3` instead.
+ * @deprecated in Godot 4.3. Use `classdb_register_extension_class4` instead.
*
* Registers an extension class in the ClassDB.
*
@@ -2754,6 +2806,7 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionCl
/**
* @name classdb_register_extension_class3
* @since 4.3
+ * @deprecated in Godot 4.4. Use `classdb_register_extension_class4` instead.
*
* Registers an extension class in the ClassDB.
*
@@ -2767,6 +2820,21 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionCl
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass3)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs);
/**
+ * @name classdb_register_extension_class4
+ * @since 4.4
+ *
+ * Registers an extension class in the ClassDB.
+ *
+ * Provided struct can be safely freed once the function returns.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_parent_class_name A pointer to a StringName with the parent class name.
+ * @param p_extension_funcs A pointer to a GDExtensionClassCreationInfo2 struct.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass4)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs);
+
+/**
* @name classdb_register_extension_class_method
* @since 4.1
*
diff --git a/core/extension/gdextension_library_loader.cpp b/core/extension/gdextension_library_loader.cpp
new file mode 100644
index 0000000000..5ba4933c35
--- /dev/null
+++ b/core/extension/gdextension_library_loader.cpp
@@ -0,0 +1,390 @@
+/**************************************************************************/
+/* gdextension_library_loader.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "gdextension_library_loader.h"
+
+#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
+#include "core/version.h"
+#include "gdextension.h"
+
+Vector<SharedObject> GDExtensionLibraryLoader::find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature) {
+ Vector<SharedObject> dependencies_shared_objects;
+ if (p_config->has_section("dependencies")) {
+ List<String> config_dependencies;
+ p_config->get_section_keys("dependencies", &config_dependencies);
+
+ for (const String &dependency : config_dependencies) {
+ Vector<String> dependency_tags = dependency.split(".");
+ bool all_tags_met = true;
+ for (int i = 0; i < dependency_tags.size(); i++) {
+ String tag = dependency_tags[i].strip_edges();
+ if (!p_has_feature(tag)) {
+ all_tags_met = false;
+ break;
+ }
+ }
+
+ if (all_tags_met) {
+ Dictionary dependency_value = p_config->get_value("dependencies", dependency);
+ for (const Variant *key = dependency_value.next(nullptr); key; key = dependency_value.next(key)) {
+ String dependency_path = *key;
+ String target_path = dependency_value[*key];
+ if (dependency_path.is_relative_path()) {
+ dependency_path = p_path.get_base_dir().path_join(dependency_path);
+ }
+ dependencies_shared_objects.push_back(SharedObject(dependency_path, dependency_tags, target_path));
+ }
+ break;
+ }
+ }
+ }
+
+ return dependencies_shared_objects;
+}
+
+String GDExtensionLibraryLoader::find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags) {
+ // First, check the explicit libraries.
+ if (p_config->has_section("libraries")) {
+ List<String> libraries;
+ p_config->get_section_keys("libraries", &libraries);
+
+ // Iterate the libraries, finding the best matching tags.
+ String best_library_path;
+ Vector<String> best_library_tags;
+ for (const String &E : libraries) {
+ Vector<String> tags = E.split(".");
+ bool all_tags_met = true;
+ for (int i = 0; i < tags.size(); i++) {
+ String tag = tags[i].strip_edges();
+ if (!p_has_feature(tag)) {
+ all_tags_met = false;
+ break;
+ }
+ }
+
+ if (all_tags_met && tags.size() > best_library_tags.size()) {
+ best_library_path = p_config->get_value("libraries", E);
+ best_library_tags = tags;
+ }
+ }
+
+ if (!best_library_path.is_empty()) {
+ if (best_library_path.is_relative_path()) {
+ best_library_path = p_path.get_base_dir().path_join(best_library_path);
+ }
+ if (r_tags != nullptr) {
+ r_tags->append_array(best_library_tags);
+ }
+ return best_library_path;
+ }
+ }
+
+ // Second, try to autodetect.
+ String autodetect_library_prefix;
+ if (p_config->has_section_key("configuration", "autodetect_library_prefix")) {
+ autodetect_library_prefix = p_config->get_value("configuration", "autodetect_library_prefix");
+ }
+ if (!autodetect_library_prefix.is_empty()) {
+ String autodetect_path = autodetect_library_prefix;
+ if (autodetect_path.is_relative_path()) {
+ autodetect_path = p_path.get_base_dir().path_join(autodetect_path);
+ }
+
+ // Find the folder and file parts of the prefix.
+ String folder;
+ String file_prefix;
+ if (DirAccess::dir_exists_absolute(autodetect_path)) {
+ folder = autodetect_path;
+ } else if (DirAccess::dir_exists_absolute(autodetect_path.get_base_dir())) {
+ folder = autodetect_path.get_base_dir();
+ file_prefix = autodetect_path.get_file();
+ } else {
+ ERR_FAIL_V_MSG(String(), vformat("Error in extension: %s. Could not find folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
+ }
+
+ // Open the folder.
+ Ref<DirAccess> dir = DirAccess::open(folder);
+ ERR_FAIL_COND_V_MSG(dir.is_null(), String(), vformat("Error in extension: %s. Could not open folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
+
+ // Iterate the files and check the prefixes, finding the best matching file.
+ String best_file;
+ Vector<String> best_file_tags;
+ dir->list_dir_begin();
+ String file_name = dir->_get_next();
+ while (file_name != "") {
+ if (!dir->current_is_dir() && file_name.begins_with(file_prefix)) {
+ // Check if the files matches all requested feature tags.
+ String tags_str = file_name.trim_prefix(file_prefix);
+ tags_str = tags_str.trim_suffix(tags_str.get_extension());
+
+ Vector<String> tags = tags_str.split(".", false);
+ bool all_tags_met = true;
+ for (int i = 0; i < tags.size(); i++) {
+ String tag = tags[i].strip_edges();
+ if (!p_has_feature(tag)) {
+ all_tags_met = false;
+ break;
+ }
+ }
+
+ // If all tags are found in the feature list, and we found more tags than before, use this file.
+ if (all_tags_met && tags.size() > best_file_tags.size()) {
+ best_file_tags = tags;
+ best_file = file_name;
+ }
+ }
+ file_name = dir->_get_next();
+ }
+
+ if (!best_file.is_empty()) {
+ String library_path = folder.path_join(best_file);
+ if (r_tags != nullptr) {
+ r_tags->append_array(best_file_tags);
+ }
+ return library_path;
+ }
+ }
+ return String();
+}
+
+Error GDExtensionLibraryLoader::open_library(const String &p_path) {
+ Error err = parse_gdextension_file(p_path);
+ if (err != OK) {
+ return err;
+ }
+
+ String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path);
+
+ Vector<String> abs_dependencies_paths;
+ if (!library_dependencies.is_empty()) {
+ for (const SharedObject &dependency : library_dependencies) {
+ abs_dependencies_paths.push_back(ProjectSettings::get_singleton()->globalize_path(dependency.path));
+ }
+ }
+
+ OS::GDExtensionData data = {
+ true, // also_set_library_path
+ &library_path, // r_resolved_path
+ Engine::get_singleton()->is_editor_hint(), // generate_temp_files
+ &abs_dependencies_paths, // library_dependencies
+ };
+
+ err = OS::get_singleton()->open_dynamic_library(is_static_library ? String() : abs_path, library, &data);
+ if (err != OK) {
+ return err;
+ }
+
+ return OK;
+}
+
+Error GDExtensionLibraryLoader::initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) {
+#ifdef TOOLS_ENABLED
+ p_extension->set_reloadable(is_reloadable && Engine::get_singleton()->is_extension_reloading_enabled());
+#endif
+
+ for (const KeyValue<String, String> &icon : class_icon_paths) {
+ p_extension->class_icon_paths[icon.key] = icon.value;
+ }
+
+ void *entry_funcptr = nullptr;
+
+ Error err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, entry_symbol, entry_funcptr, false);
+
+ if (err != OK) {
+ ERR_PRINT("GDExtension entry point '" + entry_symbol + "' not found in library " + library_path);
+ return err;
+ }
+
+ GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
+
+ GDExtensionBool ret = initialization_function(p_get_proc_address, p_extension.ptr(), r_initialization);
+
+ if (ret) {
+ return OK;
+ } else {
+ ERR_PRINT("GDExtension initialization function '" + entry_symbol + "' returned an error.");
+ return FAILED;
+ }
+}
+
+void GDExtensionLibraryLoader::close_library() {
+ OS::get_singleton()->close_dynamic_library(library);
+ library = nullptr;
+}
+
+bool GDExtensionLibraryLoader::is_library_open() const {
+ return library != nullptr;
+}
+
+bool GDExtensionLibraryLoader::has_library_changed() const {
+#ifdef TOOLS_ENABLED
+ // Check only that the last modified time is different (rather than checking
+ // that it's newer) since some OS's (namely Windows) will preserve the modified
+ // time by default when copying files.
+ if (FileAccess::get_modified_time(resource_path) != resource_last_modified_time) {
+ return true;
+ }
+ if (FileAccess::get_modified_time(library_path) != library_last_modified_time) {
+ return true;
+ }
+#endif
+ return false;
+}
+
+Error GDExtensionLibraryLoader::parse_gdextension_file(const String &p_path) {
+ resource_path = p_path;
+
+ Ref<ConfigFile> config;
+ config.instantiate();
+
+ Error err = config->load(p_path);
+
+ if (err != OK) {
+ ERR_PRINT("Error loading GDExtension configuration file: " + p_path);
+ return err;
+ }
+
+ if (!config->has_section_key("configuration", "entry_symbol")) {
+ ERR_PRINT("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: " + p_path);
+ return ERR_INVALID_DATA;
+ }
+
+ entry_symbol = config->get_value("configuration", "entry_symbol");
+
+ uint32_t compatibility_minimum[3] = { 0, 0, 0 };
+ if (config->has_section_key("configuration", "compatibility_minimum")) {
+ String compat_string = config->get_value("configuration", "compatibility_minimum");
+ Vector<int> parts = compat_string.split_ints(".");
+ for (int i = 0; i < parts.size(); i++) {
+ if (i >= 3) {
+ break;
+ }
+ if (parts[i] >= 0) {
+ compatibility_minimum[i] = parts[i];
+ }
+ }
+ } else {
+ ERR_PRINT("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: " + p_path);
+ return ERR_INVALID_DATA;
+ }
+
+ if (compatibility_minimum[0] < 4 || (compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0)) {
+ ERR_PRINT(vformat("GDExtension's compatibility_minimum (%d.%d.%d) must be at least 4.1.0: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
+ return ERR_INVALID_DATA;
+ }
+
+ bool compatible = true;
+ // Check version lexicographically.
+ if (VERSION_MAJOR != compatibility_minimum[0]) {
+ compatible = VERSION_MAJOR > compatibility_minimum[0];
+ } else if (VERSION_MINOR != compatibility_minimum[1]) {
+ compatible = VERSION_MINOR > compatibility_minimum[1];
+ } else {
+ compatible = VERSION_PATCH >= compatibility_minimum[2];
+ }
+ if (!compatible) {
+ ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
+ return ERR_INVALID_DATA;
+ }
+
+ // Optionally check maximum compatibility.
+ if (config->has_section_key("configuration", "compatibility_maximum")) {
+ uint32_t compatibility_maximum[3] = { 0, 0, 0 };
+ String compat_string = config->get_value("configuration", "compatibility_maximum");
+ Vector<int> parts = compat_string.split_ints(".");
+ for (int i = 0; i < 3; i++) {
+ if (i < parts.size() && parts[i] >= 0) {
+ compatibility_maximum[i] = parts[i];
+ } else {
+ // If a version part is missing, set the maximum to an arbitrary high value.
+ compatibility_maximum[i] = 9999;
+ }
+ }
+
+ compatible = true;
+ if (VERSION_MAJOR != compatibility_maximum[0]) {
+ compatible = VERSION_MAJOR < compatibility_maximum[0];
+ } else if (VERSION_MINOR != compatibility_maximum[1]) {
+ compatible = VERSION_MINOR < compatibility_maximum[1];
+ }
+#if VERSION_PATCH
+ // #if check to avoid -Wtype-limits warning when 0.
+ else {
+ compatible = VERSION_PATCH <= compatibility_maximum[2];
+ }
+#endif
+
+ if (!compatible) {
+ ERR_PRINT(vformat("GDExtension only compatible with Godot version %s or earlier: %s", compat_string, p_path));
+ return ERR_INVALID_DATA;
+ }
+ }
+
+ library_path = find_extension_library(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
+
+ if (library_path.is_empty()) {
+ const String os_arch = OS::get_singleton()->get_name().to_lower() + "." + Engine::get_singleton()->get_architecture_name();
+ ERR_PRINT(vformat("No GDExtension library found for current OS and architecture (%s) in configuration file: %s", os_arch, p_path));
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ is_static_library = library_path.ends_with(".a") || library_path.ends_with(".xcframework");
+
+ if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
+ library_path = p_path.get_base_dir().path_join(library_path);
+ }
+
+#ifdef TOOLS_ENABLED
+ is_reloadable = config->get_value("configuration", "reloadable", false);
+
+ update_last_modified_time(
+ FileAccess::get_modified_time(resource_path),
+ FileAccess::get_modified_time(library_path));
+#endif
+
+ library_dependencies = find_extension_dependencies(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
+
+ // Handle icons if any are specified.
+ if (config->has_section("icons")) {
+ List<String> keys;
+ config->get_section_keys("icons", &keys);
+ for (const String &key : keys) {
+ String icon_path = config->get_value("icons", key);
+ if (icon_path.is_relative_path()) {
+ icon_path = p_path.get_base_dir().path_join(icon_path);
+ }
+
+ class_icon_paths[key] = icon_path;
+ }
+ }
+
+ return OK;
+}
diff --git a/core/extension/gdextension_library_loader.h b/core/extension/gdextension_library_loader.h
new file mode 100644
index 0000000000..f4372a75d4
--- /dev/null
+++ b/core/extension/gdextension_library_loader.h
@@ -0,0 +1,84 @@
+/**************************************************************************/
+/* gdextension_library_loader.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef GDEXTENSION_LIBRARY_LOADER_H
+#define GDEXTENSION_LIBRARY_LOADER_H
+
+#include <functional>
+
+#include "core/extension/gdextension_loader.h"
+#include "core/io/config_file.h"
+#include "core/os/shared_object.h"
+
+class GDExtensionLibraryLoader : public GDExtensionLoader {
+ friend class GDExtensionManager;
+ friend class GDExtension;
+
+private:
+ String resource_path;
+
+ void *library = nullptr; // pointer if valid.
+ String library_path;
+ String entry_symbol;
+
+ bool is_static_library = false;
+
+#ifdef TOOLS_ENABLED
+ bool is_reloadable = false;
+#endif
+
+ Vector<SharedObject> library_dependencies;
+
+ HashMap<String, String> class_icon_paths;
+
+#ifdef TOOLS_ENABLED
+ uint64_t resource_last_modified_time = 0;
+ uint64_t library_last_modified_time = 0;
+
+ void update_last_modified_time(uint64_t p_resource_last_modified_time, uint64_t p_library_last_modified_time) {
+ resource_last_modified_time = p_resource_last_modified_time;
+ library_last_modified_time = p_library_last_modified_time;
+ }
+#endif
+
+public:
+ static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);
+ static Vector<SharedObject> find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature);
+
+ virtual Error open_library(const String &p_path) override;
+ virtual Error initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) override;
+ virtual void close_library() override;
+ virtual bool is_library_open() const override;
+ virtual bool has_library_changed() const override;
+
+ Error parse_gdextension_file(const String &p_path);
+};
+
+#endif // GDEXTENSION_LIBRARY_LOADER_H
diff --git a/core/extension/gdextension_loader.h b/core/extension/gdextension_loader.h
new file mode 100644
index 0000000000..7d779858b7
--- /dev/null
+++ b/core/extension/gdextension_loader.h
@@ -0,0 +1,47 @@
+/**************************************************************************/
+/* gdextension_loader.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef GDEXTENSION_LOADER_H
+#define GDEXTENSION_LOADER_H
+
+#include "core/object/ref_counted.h"
+
+class GDExtension;
+
+class GDExtensionLoader : public RefCounted {
+public:
+ virtual Error open_library(const String &p_path) = 0;
+ virtual Error initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) = 0;
+ virtual void close_library() = 0;
+ virtual bool is_library_open() const = 0;
+ virtual bool has_library_changed() const = 0;
+};
+
+#endif // GDEXTENSION_LOADER_H
diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp
index 1ee9de0776..eeae6b1996 100644
--- a/core/extension/gdextension_manager.cpp
+++ b/core/extension/gdextension_manager.cpp
@@ -31,6 +31,7 @@
#include "gdextension_manager.h"
#include "core/extension/gdextension_compat_hashes.h"
+#include "core/extension/gdextension_library_loader.h"
#include "core/io/file_access.h"
#include "core/object/script_language.h"
@@ -69,11 +70,22 @@ GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(co
}
GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &p_path) {
+ Ref<GDExtensionLibraryLoader> loader;
+ loader.instantiate();
+ return GDExtensionManager::get_singleton()->load_extension_with_loader(p_path, loader);
+}
+
+GDExtensionManager::LoadStatus GDExtensionManager::load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader) {
+ DEV_ASSERT(p_loader.is_valid());
+
if (gdextension_map.has(p_path)) {
return LOAD_STATUS_ALREADY_LOADED;
}
- Ref<GDExtension> extension = ResourceLoader::load(p_path);
- if (extension.is_null()) {
+
+ Ref<GDExtension> extension;
+ extension.instantiate();
+ Error err = extension->open_library(p_path, p_loader);
+ if (err != OK) {
return LOAD_STATUS_FAILED;
}
@@ -82,6 +94,7 @@ GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &
return status;
}
+ extension->set_path(p_path);
gdextension_map[p_path] = extension;
return LOAD_STATUS_OK;
}
@@ -117,7 +130,7 @@ GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String
extension->close_library();
}
- Error err = GDExtensionResourceLoader::load_gdextension_resource(p_path, extension);
+ Error err = extension->open_library(p_path, extension->loader);
if (err != OK) {
return LOAD_STATUS_FAILED;
}
diff --git a/core/extension/gdextension_manager.h b/core/extension/gdextension_manager.h
index 9386e356bb..b488189604 100644
--- a/core/extension/gdextension_manager.h
+++ b/core/extension/gdextension_manager.h
@@ -63,6 +63,7 @@ private:
public:
LoadStatus load_extension(const String &p_path);
+ LoadStatus load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader);
LoadStatus reload_extension(const String &p_path);
LoadStatus unload_extension(const String &p_path);
bool is_extension_loaded(const String &p_path) const;
diff --git a/core/io/dtls_server.cpp b/core/io/dtls_server.cpp
index 07d62d3a8d..7638328dc3 100644
--- a/core/io/dtls_server.cpp
+++ b/core/io/dtls_server.cpp
@@ -33,12 +33,12 @@
#include "core/config/project_settings.h"
#include "core/io/file_access.h"
-DTLSServer *(*DTLSServer::_create)() = nullptr;
+DTLSServer *(*DTLSServer::_create)(bool p_notify_postinitialize) = nullptr;
bool DTLSServer::available = false;
-DTLSServer *DTLSServer::create() {
+DTLSServer *DTLSServer::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
return nullptr;
}
diff --git a/core/io/dtls_server.h b/core/io/dtls_server.h
index f3fbde3c15..5ffed1ecc3 100644
--- a/core/io/dtls_server.h
+++ b/core/io/dtls_server.h
@@ -38,14 +38,14 @@ class DTLSServer : public RefCounted {
GDCLASS(DTLSServer, RefCounted);
protected:
- static DTLSServer *(*_create)();
+ static DTLSServer *(*_create)(bool p_notify_postinitialize);
static void _bind_methods();
static bool available;
public:
static bool is_available();
- static DTLSServer *create();
+ static DTLSServer *create(bool p_notify_postinitialize = true);
virtual Error setup(Ref<TLSOptions> p_options) = 0;
virtual void stop() = 0;
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index 1cf388b33a..c857d54925 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -59,11 +59,9 @@ bool FileAccess::exists(const String &p_name) {
return true;
}
- Ref<FileAccess> f = open(p_name, READ);
- if (f.is_null()) {
- return false;
- }
- return true;
+ // Using file_exists because it's faster than trying to open the file.
+ Ref<FileAccess> ret = create_for_path(p_name);
+ return ret->file_exists(p_name);
}
void FileAccess::_set_access_type(AccessType p_access) {
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 833fd1adc3..fc91341bed 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -42,9 +42,9 @@ const char *HTTPClient::_methods[METHOD_MAX] = {
"PATCH"
};
-HTTPClient *HTTPClient::create() {
+HTTPClient *HTTPClient::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
return nullptr;
}
diff --git a/core/io/http_client.h b/core/io/http_client.h
index 9e018182e3..5945291122 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -158,12 +158,12 @@ protected:
Error _request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body);
Error _request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body = String());
- static HTTPClient *(*_create)();
+ static HTTPClient *(*_create)(bool p_notify_postinitialize);
static void _bind_methods();
public:
- static HTTPClient *create();
+ static HTTPClient *create(bool p_notify_postinitialize = true);
String query_string_from_dict(const Dictionary &p_dict);
Error verify_headers(const Vector<String> &p_headers);
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index 2f45238951..70fcad543a 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -35,8 +35,8 @@
#include "core/io/stream_peer_tls.h"
#include "core/version.h"
-HTTPClient *HTTPClientTCP::_create_func() {
- return memnew(HTTPClientTCP);
+HTTPClient *HTTPClientTCP::_create_func(bool p_notify_postinitialize) {
+ return static_cast<HTTPClient *>(ClassDB::creator<HTTPClientTCP>(p_notify_postinitialize));
}
Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, Ref<TLSOptions> p_options) {
@@ -792,6 +792,6 @@ HTTPClientTCP::HTTPClientTCP() {
request_buffer.instantiate();
}
-HTTPClient *(*HTTPClient::_create)() = HTTPClientTCP::_create_func;
+HTTPClient *(*HTTPClient::_create)(bool p_notify_postinitialize) = HTTPClientTCP::_create_func;
#endif // WEB_ENABLED
diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h
index 6060c975bc..dd6cc6b84f 100644
--- a/core/io/http_client_tcp.h
+++ b/core/io/http_client_tcp.h
@@ -76,7 +76,7 @@ private:
Error _get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received);
public:
- static HTTPClient *_create_func();
+ static HTTPClient *_create_func(bool p_notify_postinitialize);
Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override;
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index f20d65bef9..38c71b19fa 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -81,17 +81,17 @@ struct _IP_ResolverPrivate {
continue;
}
- mutex.lock();
+ MutexLock lock(mutex);
List<IPAddress> response;
String hostname = queue[i].hostname;
IP::Type type = queue[i].type;
- mutex.unlock();
+ lock.temp_unlock();
// We should not lock while resolving the hostname,
// only when modifying the queue.
IP::get_singleton()->_resolve_hostname(response, hostname, type);
- MutexLock lock(mutex);
+ lock.temp_relock();
// Could have been completed by another function, or deleted.
if (queue[i].status.get() != IP::RESOLVER_STATUS_WAITING) {
continue;
@@ -131,21 +131,22 @@ PackedStringArray IP::resolve_hostname_addresses(const String &p_hostname, Type
List<IPAddress> res;
String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type);
- resolver->mutex.lock();
- if (resolver->cache.has(key)) {
- res = resolver->cache[key];
- } else {
- // This should be run unlocked so the resolver thread can keep resolving
- // other requests.
- resolver->mutex.unlock();
- _resolve_hostname(res, p_hostname, p_type);
- resolver->mutex.lock();
- // We might be overriding another result, but we don't care as long as the result is valid.
- if (res.size()) {
- resolver->cache[key] = res;
+ {
+ MutexLock lock(resolver->mutex);
+ if (resolver->cache.has(key)) {
+ res = resolver->cache[key];
+ } else {
+ // This should be run unlocked so the resolver thread can keep resolving
+ // other requests.
+ lock.temp_unlock();
+ _resolve_hostname(res, p_hostname, p_type);
+ lock.temp_relock();
+ // We might be overriding another result, but we don't care as long as the result is valid.
+ if (res.size()) {
+ resolver->cache[key] = res;
+ }
}
}
- resolver->mutex.unlock();
PackedStringArray result;
for (const IPAddress &E : res) {
diff --git a/core/io/packet_peer_dtls.cpp b/core/io/packet_peer_dtls.cpp
index 18bef3ff3c..231c48d887 100644
--- a/core/io/packet_peer_dtls.cpp
+++ b/core/io/packet_peer_dtls.cpp
@@ -32,12 +32,12 @@
#include "core/config/project_settings.h"
#include "core/io/file_access.h"
-PacketPeerDTLS *(*PacketPeerDTLS::_create)() = nullptr;
+PacketPeerDTLS *(*PacketPeerDTLS::_create)(bool p_notify_postinitialize) = nullptr;
bool PacketPeerDTLS::available = false;
-PacketPeerDTLS *PacketPeerDTLS::create() {
+PacketPeerDTLS *PacketPeerDTLS::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
return nullptr;
}
diff --git a/core/io/packet_peer_dtls.h b/core/io/packet_peer_dtls.h
index 3990a851f7..03d97a5903 100644
--- a/core/io/packet_peer_dtls.h
+++ b/core/io/packet_peer_dtls.h
@@ -38,7 +38,7 @@ class PacketPeerDTLS : public PacketPeer {
GDCLASS(PacketPeerDTLS, PacketPeer);
protected:
- static PacketPeerDTLS *(*_create)();
+ static PacketPeerDTLS *(*_create)(bool p_notify_postinitialize);
static void _bind_methods();
static bool available;
@@ -57,7 +57,7 @@ public:
virtual void disconnect_from_peer() = 0;
virtual Status get_status() const = 0;
- static PacketPeerDTLS *create();
+ static PacketPeerDTLS *create(bool p_notify_postinitialize = true);
static bool is_available();
PacketPeerDTLS() {}
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index 432adb88da..598c99c188 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -416,21 +416,15 @@ void Resource::_take_over_path(const String &p_path) {
}
RID Resource::get_rid() const {
- if (get_script_instance()) {
- Callable::CallError ce;
- RID ret = get_script_instance()->callp(SNAME("_get_rid"), nullptr, 0, ce);
- if (ce.error == Callable::CallError::CALL_OK && ret.is_valid()) {
- return ret;
- }
- }
- if (_get_extension() && _get_extension()->get_rid) {
- RID ret = RID::from_uint64(_get_extension()->get_rid(_get_extension_instance()));
- if (ret.is_valid()) {
- return ret;
+ RID ret;
+ if (!GDVIRTUAL_CALL(_get_rid, ret)) {
+#ifndef DISABLE_DEPRECATED
+ if (_get_extension() && _get_extension()->get_rid) {
+ ret = RID::from_uint64(_get_extension()->get_rid(_get_extension_instance()));
}
+#endif
}
-
- return RID();
+ return ret;
}
#ifdef TOOLS_ENABLED
@@ -558,11 +552,8 @@ void Resource::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_name"), "set_name", "get_name");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_scene_unique_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_scene_unique_id", "get_scene_unique_id");
- MethodInfo get_rid_bind("_get_rid");
- get_rid_bind.return_val.type = Variant::RID;
-
- ::ClassDB::add_virtual_method(get_class_static(), get_rid_bind, true, Vector<String>(), true);
GDVIRTUAL_BIND(_setup_local_to_scene);
+ GDVIRTUAL_BIND(_get_rid);
}
Resource::Resource() :
diff --git a/core/io/resource.h b/core/io/resource.h
index cc8a0d4387..2c1a431255 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -87,6 +87,8 @@ protected:
virtual void reset_local_to_scene();
GDVIRTUAL0(_setup_local_to_scene);
+ GDVIRTUAL0RC(RID, _get_rid);
+
public:
static Node *(*_get_local_scene_func)(); //used by editor
static void (*_update_configuration_warning)(); //used by editor
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index 9e6f3ba314..b4c43abe00 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -364,6 +364,23 @@ ResourceUID::ID ResourceFormatImporter::get_resource_uid(const String &p_path) c
return pat.uid;
}
+Error ResourceFormatImporter::get_resource_import_info(const String &p_path, StringName &r_type, ResourceUID::ID &r_uid, String &r_import_group_file) const {
+ PathAndType pat;
+ Error err = _get_path_and_type(p_path, pat);
+
+ if (err == OK) {
+ r_type = pat.type;
+ r_uid = pat.uid;
+ r_import_group_file = pat.group_file;
+ } else {
+ r_type = "";
+ r_uid = ResourceUID::INVALID_ID;
+ r_import_group_file = "";
+ }
+
+ return err;
+}
+
Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) const {
PathAndType pat;
Error err = _get_path_and_type(p_path, pat);
diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h
index dbd9e70d16..7b1806c3d2 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -93,6 +93,7 @@ public:
String get_import_settings_hash() const;
String get_import_base_path(const String &p_for_file) const;
+ Error get_resource_import_info(const String &p_path, StringName &r_type, ResourceUID::ID &r_uid, String &r_import_group_file) const;
ResourceFormatImporter();
};
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index c9ed4e27d9..d2c4668d12 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -207,34 +207,53 @@ void ResourceFormatLoader::_bind_methods() {
///////////////////////////////////
+// These are used before and after a wait for a WorkerThreadPool task
+// because that can lead to another load started in the same thread,
+// something we must treat as a different stack for the purposes
+// of tracking nesting.
+
+#define PREPARE_FOR_WTP_WAIT \
+ int load_nesting_backup = ResourceLoader::load_nesting; \
+ Vector<String> load_paths_stack_backup = ResourceLoader::load_paths_stack; \
+ ResourceLoader::load_nesting = 0; \
+ ResourceLoader::load_paths_stack.clear();
+
+#define RESTORE_AFTER_WTP_WAIT \
+ DEV_ASSERT(ResourceLoader::load_nesting == 0); \
+ DEV_ASSERT(ResourceLoader::load_paths_stack.is_empty()); \
+ ResourceLoader::load_nesting = load_nesting_backup; \
+ ResourceLoader::load_paths_stack = load_paths_stack_backup; \
+ load_paths_stack_backup.clear();
+
// This should be robust enough to be called redundantly without issues.
void ResourceLoader::LoadToken::clear() {
thread_load_mutex.lock();
WorkerThreadPool::TaskID task_to_await = 0;
+ // User-facing tokens shouldn't be deleted until completely claimed.
+ DEV_ASSERT(user_rc == 0 && user_path.is_empty());
+
if (!local_path.is_empty()) { // Empty is used for the special case where the load task is not registered.
DEV_ASSERT(thread_load_tasks.has(local_path));
ThreadLoadTask &load_task = thread_load_tasks[local_path];
- if (!load_task.awaited) {
+ if (load_task.task_id && !load_task.awaited) {
task_to_await = load_task.task_id;
- load_task.awaited = true;
}
+ // Removing a task which is still in progress would be catastrophic.
+ // Tokens must be alive until the task thread function is done.
+ DEV_ASSERT(load_task.status == THREAD_LOAD_FAILED || load_task.status == THREAD_LOAD_LOADED);
thread_load_tasks.erase(local_path);
local_path.clear();
}
- if (!user_path.is_empty()) {
- DEV_ASSERT(user_load_tokens.has(user_path));
- user_load_tokens.erase(user_path);
- user_path.clear();
- }
-
thread_load_mutex.unlock();
// If task is unused, await it here, locally, now the token data is consistent.
if (task_to_await) {
+ PREPARE_FOR_WTP_WAIT
WorkerThreadPool::get_singleton()->wait_for_task_completion(task_to_await);
+ RESTORE_AFTER_WTP_WAIT
}
}
@@ -295,11 +314,11 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
ERR_FAIL_V_MSG(Ref<Resource>(), vformat("No loader found for resource: %s (expected type: %s)", p_path, p_type_hint));
}
-void ResourceLoader::_thread_load_function(void *p_userdata) {
+// This implementation must allow re-entrancy for a task that started awaiting in a deeper stack frame.
+void ResourceLoader::_run_load_task(void *p_userdata) {
ThreadLoadTask &load_task = *(ThreadLoadTask *)p_userdata;
thread_load_mutex.lock();
- caller_task_id = load_task.task_id;
if (cleaning_tasks) {
load_task.status = THREAD_LOAD_FAILED;
thread_load_mutex.unlock();
@@ -322,8 +341,10 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
}
// --
+ bool xl_remapped = false;
+ const String &remapped_path = _path_remap(load_task.local_path, &xl_remapped);
Error load_err = OK;
- Ref<Resource> res = _load(load_task.remapped_path, load_task.remapped_path != load_task.local_path ? load_task.local_path : String(), load_task.type_hint, load_task.cache_mode, &load_err, load_task.use_sub_threads, &load_task.progress);
+ Ref<Resource> res = _load(remapped_path, remapped_path != load_task.local_path ? load_task.local_path : String(), load_task.type_hint, load_task.cache_mode, &load_err, load_task.use_sub_threads, &load_task.progress);
if (MessageQueue::get_singleton() != MessageQueue::get_main_singleton()) {
MessageQueue::get_singleton()->flush();
}
@@ -356,27 +377,40 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
unlock_pending = false;
if (!ignoring) {
- if (replacing) {
- Ref<Resource> old_res = ResourceCache::get_ref(load_task.local_path);
- if (old_res.is_valid() && old_res != load_task.resource) {
- // If resource is already loaded, only replace its data, to avoid existing invalidating instances.
- old_res->copy_from(load_task.resource);
+ ResourceCache::lock.lock(); // Check and operations must happen atomically.
+ bool pending_unlock = true;
+ Ref<Resource> old_res = ResourceCache::get_ref(load_task.local_path);
+ if (old_res.is_valid()) {
+ if (old_res != load_task.resource) {
+ // Resource can already exists at this point for two reasons:
+ // a) The load uses replace mode.
+ // b) There were more than one load in flight for the same path because of deadlock prevention.
+ // Either case, we want to keep the resource that was already there.
+ ResourceCache::lock.unlock();
+ pending_unlock = false;
+ if (replacing) {
+ old_res->copy_from(load_task.resource);
+ }
load_task.resource = old_res;
}
+ } else {
+ load_task.resource->set_path(load_task.local_path);
+ }
+ if (pending_unlock) {
+ ResourceCache::lock.unlock();
}
- load_task.resource->set_path(load_task.local_path, replacing);
} else {
load_task.resource->set_path_cache(load_task.local_path);
}
- if (load_task.xl_remapped) {
+ if (xl_remapped) {
load_task.resource->set_as_translation_remapped(true);
}
#ifdef TOOLS_ENABLED
load_task.resource->set_edited(false);
if (timestamp_on_load) {
- uint64_t mt = FileAccess::get_modified_time(load_task.remapped_path);
+ uint64_t mt = FileAccess::get_modified_time(remapped_path);
//printf("mt %s: %lli\n",remapped_path.utf8().get_data(),mt);
load_task.resource->set_last_modified_time(mt);
}
@@ -426,36 +460,44 @@ static String _validate_local_path(const String &p_path) {
}
Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode) {
- thread_load_mutex.lock();
- if (user_load_tokens.has(p_path)) {
- print_verbose("load_threaded_request(): Another threaded load for resource path '" + p_path + "' has been initiated. Not an error.");
- user_load_tokens[p_path]->reference(); // Additional request.
- thread_load_mutex.unlock();
- return OK;
- }
- user_load_tokens[p_path] = nullptr;
- thread_load_mutex.unlock();
+ Ref<ResourceLoader::LoadToken> token = _load_start(p_path, p_type_hint, p_use_sub_threads ? LOAD_THREAD_DISTRIBUTE : LOAD_THREAD_SPAWN_SINGLE, p_cache_mode, true);
+ return token.is_valid() ? OK : FAILED;
+}
- Ref<ResourceLoader::LoadToken> token = _load_start(p_path, p_type_hint, p_use_sub_threads ? LOAD_THREAD_DISTRIBUTE : LOAD_THREAD_SPAWN_SINGLE, p_cache_mode);
- if (token.is_valid()) {
- thread_load_mutex.lock();
- token->user_path = p_path;
- token->reference(); // First request.
- user_load_tokens[p_path] = token.ptr();
- print_lt("REQUEST: user load tokens: " + itos(user_load_tokens.size()));
- thread_load_mutex.unlock();
- return OK;
+ResourceLoader::LoadToken *ResourceLoader::_load_threaded_request_reuse_user_token(const String &p_path) {
+ HashMap<String, LoadToken *>::Iterator E = user_load_tokens.find(p_path);
+ if (E) {
+ print_verbose("load_threaded_request(): Another threaded load for resource path '" + p_path + "' has been initiated. Not an error.");
+ LoadToken *token = E->value;
+ token->user_rc++;
+ return token;
} else {
- return FAILED;
+ return nullptr;
}
}
+void ResourceLoader::_load_threaded_request_setup_user_token(LoadToken *p_token, const String &p_path) {
+ p_token->user_path = p_path;
+ p_token->reference(); // Extra RC until all user requests have been gotten.
+ p_token->user_rc = 1;
+ user_load_tokens[p_path] = p_token;
+ print_lt("REQUEST: user load tokens: " + itos(user_load_tokens.size()));
+}
+
Ref<Resource> ResourceLoader::load(const String &p_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error) {
if (r_error) {
*r_error = OK;
}
- Ref<LoadToken> load_token = _load_start(p_path, p_type_hint, LOAD_THREAD_FROM_CURRENT, p_cache_mode);
+ LoadThreadMode thread_mode = LOAD_THREAD_FROM_CURRENT;
+ if (WorkerThreadPool::get_singleton()->get_caller_task_id() != WorkerThreadPool::INVALID_TASK_ID) {
+ // If user is initiating a single-threaded load from a WorkerThreadPool task,
+ // we instead spawn a new task so there's a precondition that a load in a pool task
+ // is always initiated by the engine. That makes certain aspects simpler, such as
+ // cyclic load detection and awaiting.
+ thread_mode = LOAD_THREAD_SPAWN_SINGLE;
+ }
+ Ref<LoadToken> load_token = _load_start(p_path, p_type_hint, thread_mode, p_cache_mode);
if (!load_token.is_valid()) {
if (r_error) {
*r_error = FAILED;
@@ -467,7 +509,7 @@ Ref<Resource> ResourceLoader::load(const String &p_path, const String &p_type_hi
return res;
}
-Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode) {
+Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode, bool p_for_user) {
String local_path = _validate_local_path(p_path);
bool ignoring_cache = p_cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE || p_cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP;
@@ -480,6 +522,13 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path,
{
MutexLock thread_load_lock(thread_load_mutex);
+ if (p_for_user) {
+ LoadToken *existing_token = _load_threaded_request_reuse_user_token(p_path);
+ if (existing_token) {
+ return Ref<LoadToken>(existing_token);
+ }
+ }
+
if (!ignoring_cache && thread_load_tasks.has(local_path)) {
load_token = Ref<LoadToken>(thread_load_tasks[local_path].load_token);
if (load_token.is_valid()) {
@@ -493,12 +542,14 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path,
load_token.instantiate();
load_token->local_path = local_path;
+ if (p_for_user) {
+ _load_threaded_request_setup_user_token(load_token.ptr(), p_path);
+ }
//create load task
{
ThreadLoadTask load_task;
- load_task.remapped_path = _path_remap(local_path, &load_task.xl_remapped);
load_task.load_token = load_token.ptr();
load_task.local_path = local_path;
load_task.type_hint = p_type_hint;
@@ -511,6 +562,7 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path,
load_task.resource = existing;
load_task.status = THREAD_LOAD_LOADED;
load_task.progress = 1.0;
+ DEV_ASSERT(!thread_load_tasks.has(local_path));
thread_load_tasks[local_path] = load_task;
return load_token;
}
@@ -532,14 +584,20 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path,
run_on_current_thread = must_not_register || p_thread_mode == LOAD_THREAD_FROM_CURRENT;
if (run_on_current_thread) {
- load_task_ptr->thread_id = Thread::get_caller_id();
+ // The current thread may happen to be a thread from the pool.
+ WorkerThreadPool::TaskID tid = WorkerThreadPool::get_singleton()->get_caller_task_id();
+ if (tid != WorkerThreadPool::INVALID_TASK_ID) {
+ load_task_ptr->task_id = tid;
+ } else {
+ load_task_ptr->thread_id = Thread::get_caller_id();
+ }
} else {
- load_task_ptr->task_id = WorkerThreadPool::get_singleton()->add_native_task(&ResourceLoader::_thread_load_function, load_task_ptr);
+ load_task_ptr->task_id = WorkerThreadPool::get_singleton()->add_native_task(&ResourceLoader::_run_load_task, load_task_ptr);
}
- }
+ } // MutexLock(thread_load_mutex).
if (run_on_current_thread) {
- _thread_load_function(load_task_ptr);
+ _run_load_task(load_task_ptr);
if (must_not_register) {
load_token->res_if_unregistered = load_task_ptr->resource;
}
@@ -626,22 +684,16 @@ Ref<Resource> ResourceLoader::load_threaded_get(const String &p_path, Error *r_e
}
LoadToken *load_token = user_load_tokens[p_path];
- if (!load_token) {
- // This happens if requested from one thread and rapidly querying from another.
- if (r_error) {
- *r_error = ERR_BUSY;
- }
- return Ref<Resource>();
- }
+ DEV_ASSERT(load_token->user_rc >= 1);
// Support userland requesting on the main thread before the load is reported to be complete.
if (Thread::is_main_thread() && !load_token->local_path.is_empty()) {
const ThreadLoadTask &load_task = thread_load_tasks[load_token->local_path];
while (load_task.status == THREAD_LOAD_IN_PROGRESS) {
- thread_load_lock.~MutexLock();
+ thread_load_lock.temp_unlock();
bool exit = !_ensure_load_progress();
OS::get_singleton()->delay_usec(1000);
- new (&thread_load_lock) MutexLock(thread_load_mutex);
+ thread_load_lock.temp_relock();
if (exit) {
break;
}
@@ -649,8 +701,15 @@ Ref<Resource> ResourceLoader::load_threaded_get(const String &p_path, Error *r_e
}
res = _load_complete_inner(*load_token, r_error, thread_load_lock);
- if (load_token->unreference()) {
- memdelete(load_token);
+
+ load_token->user_rc--;
+ if (load_token->user_rc == 0) {
+ load_token->user_path.clear();
+ user_load_tokens.erase(p_path);
+ if (load_token->unreference()) {
+ memdelete(load_token);
+ load_token = nullptr;
+ }
}
}
@@ -682,7 +741,7 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro
if (load_task.status == THREAD_LOAD_IN_PROGRESS) {
DEV_ASSERT((load_task.task_id == 0) != (load_task.thread_id == 0));
- if ((load_task.task_id != 0 && load_task.task_id == caller_task_id) ||
+ if ((load_task.task_id != 0 && load_task.task_id == WorkerThreadPool::get_singleton()->get_caller_task_id()) ||
(load_task.thread_id != 0 && load_task.thread_id == Thread::get_caller_id())) {
// Load is in progress, but it's precisely this thread the one in charge.
// That means this is a cyclic load.
@@ -693,55 +752,45 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro
}
bool loader_is_wtp = load_task.task_id != 0;
- Error wtp_task_err = FAILED;
if (loader_is_wtp) {
// Loading thread is in the worker pool.
+ p_thread_load_lock.temp_unlock();
+
+ PREPARE_FOR_WTP_WAIT
+ Error wait_err = WorkerThreadPool::get_singleton()->wait_for_task_completion(load_task.task_id);
+ RESTORE_AFTER_WTP_WAIT
+
+ DEV_ASSERT(!wait_err || wait_err == ERR_BUSY);
+ if (wait_err == ERR_BUSY) {
+ // The WorkerThreadPool has reported that the current task wants to await on an older one.
+ // That't not allowed for safety, to avoid deadlocks. Fortunately, though, in the context of
+ // resource loading that means that the task to wait for can be restarted here to break the
+ // cycle, with as much recursion into this process as needed.
+ // When the stack is eventually unrolled, the original load will have been notified to go on.
+ _run_load_task(&load_task);
+ }
+
+ p_thread_load_lock.temp_relock();
load_task.awaited = true;
- thread_load_mutex.unlock();
- wtp_task_err = WorkerThreadPool::get_singleton()->wait_for_task_completion(load_task.task_id);
- }
- if (load_task.status == THREAD_LOAD_IN_PROGRESS) { // If early errored, awaiting would deadlock.
- if (loader_is_wtp) {
- if (wtp_task_err == ERR_BUSY) {
- // The WorkerThreadPool has reported that the current task wants to await on an older one.
- // That't not allowed for safety, to avoid deadlocks. Fortunately, though, in the context of
- // resource loading that means that the task to wait for can be restarted here to break the
- // cycle, with as much recursion into this process as needed.
- // When the stack is eventually unrolled, the original load will have been notified to go on.
- // CACHE_MODE_IGNORE is needed because, otherwise, the new request would just see there's
- // an ongoing load for that resource and wait for it again. This value forces a new load.
- Ref<ResourceLoader::LoadToken> token = _load_start(load_task.local_path, load_task.type_hint, LOAD_THREAD_DISTRIBUTE, ResourceFormatLoader::CACHE_MODE_IGNORE);
- Ref<Resource> resource = _load_complete(*token.ptr(), &wtp_task_err);
- if (r_error) {
- *r_error = wtp_task_err;
- }
- thread_load_mutex.lock();
- return resource;
- } else {
- DEV_ASSERT(wtp_task_err == OK);
- thread_load_mutex.lock();
- }
- } else if (load_task.need_wait) {
- // Loading thread is main or user thread.
- if (!load_task.cond_var) {
- load_task.cond_var = memnew(ConditionVariable);
- }
- load_task.awaiters_count++;
- do {
- load_task.cond_var->wait(p_thread_load_lock);
- DEV_ASSERT(thread_load_tasks.has(p_load_token.local_path) && p_load_token.get_reference_count());
- } while (load_task.need_wait);
- load_task.awaiters_count--;
- if (load_task.awaiters_count == 0) {
- memdelete(load_task.cond_var);
- load_task.cond_var = nullptr;
- }
+ DEV_ASSERT(load_task.status == THREAD_LOAD_FAILED || load_task.status == THREAD_LOAD_LOADED);
+ } else if (load_task.need_wait) {
+ // Loading thread is main or user thread.
+ if (!load_task.cond_var) {
+ load_task.cond_var = memnew(ConditionVariable);
}
- } else {
- if (loader_is_wtp) {
- thread_load_mutex.lock();
+ load_task.awaiters_count++;
+ do {
+ load_task.cond_var->wait(p_thread_load_lock);
+ DEV_ASSERT(thread_load_tasks.has(p_load_token.local_path) && p_load_token.get_reference_count());
+ } while (load_task.need_wait);
+ load_task.awaiters_count--;
+ if (load_task.awaiters_count == 0) {
+ memdelete(load_task.cond_var);
+ load_task.cond_var = nullptr;
}
+
+ DEV_ASSERT(load_task.status == THREAD_LOAD_FAILED || load_task.status == THREAD_LOAD_LOADED);
}
}
@@ -1055,36 +1104,39 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem
new_path = path_remaps[new_path];
} else {
// Try file remap.
- Error err;
- Ref<FileAccess> f = FileAccess::open(new_path + ".remap", FileAccess::READ, &err);
- if (f.is_valid()) {
- VariantParser::StreamFile stream;
- stream.f = f;
-
- String assign;
- Variant value;
- VariantParser::Tag next_tag;
-
- int lines = 0;
- String error_text;
- while (true) {
- assign = Variant();
- next_tag.fields.clear();
- next_tag.name = String();
-
- err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true);
- if (err == ERR_FILE_EOF) {
- break;
- } else if (err != OK) {
- ERR_PRINT("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + ".");
- break;
- }
+ // Usually, there's no remap file and FileAccess::exists() is faster than FileAccess::open().
+ if (FileAccess::exists(new_path + ".remap")) {
+ Error err;
+ Ref<FileAccess> f = FileAccess::open(new_path + ".remap", FileAccess::READ, &err);
+ if (f.is_valid()) {
+ VariantParser::StreamFile stream;
+ stream.f = f;
+
+ String assign;
+ Variant value;
+ VariantParser::Tag next_tag;
+
+ int lines = 0;
+ String error_text;
+ while (true) {
+ assign = Variant();
+ next_tag.fields.clear();
+ next_tag.name = String();
+
+ err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true);
+ if (err == ERR_FILE_EOF) {
+ break;
+ } else if (err != OK) {
+ ERR_PRINT("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + ".");
+ break;
+ }
- if (assign == "path") {
- new_path = value;
- break;
- } else if (next_tag.name != "remap") {
- break;
+ if (assign == "path") {
+ new_path = value;
+ break;
+ } else if (next_tag.name != "remap") {
+ break;
+ }
}
}
}
@@ -1156,7 +1208,7 @@ void ResourceLoader::clear_translation_remaps() {
void ResourceLoader::clear_thread_load_tasks() {
// Bring the thing down as quickly as possible without causing deadlocks or leaks.
- thread_load_mutex.lock();
+ MutexLock thread_load_lock(thread_load_mutex);
cleaning_tasks = true;
while (true) {
@@ -1175,21 +1227,23 @@ void ResourceLoader::clear_thread_load_tasks() {
if (none_running) {
break;
}
- thread_load_mutex.unlock();
+ thread_load_lock.temp_unlock();
OS::get_singleton()->delay_usec(1000);
- thread_load_mutex.lock();
+ thread_load_lock.temp_relock();
}
while (user_load_tokens.begin()) {
- // User load tokens remove themselves from the map on destruction.
- memdelete(user_load_tokens.begin()->value);
+ LoadToken *user_token = user_load_tokens.begin()->value;
+ user_load_tokens.remove(user_load_tokens.begin());
+ DEV_ASSERT(user_token->user_rc > 0 && !user_token->user_path.is_empty());
+ user_token->user_path.clear();
+ user_token->user_rc = 0;
+ user_token->unreference();
}
- user_load_tokens.clear();
thread_load_tasks.clear();
cleaning_tasks = false;
- thread_load_mutex.unlock();
}
void ResourceLoader::load_path_remaps() {
@@ -1302,12 +1356,15 @@ bool ResourceLoader::abort_on_missing_resource = true;
bool ResourceLoader::timestamp_on_load = false;
thread_local int ResourceLoader::load_nesting = 0;
-thread_local WorkerThreadPool::TaskID ResourceLoader::caller_task_id = 0;
thread_local Vector<String> ResourceLoader::load_paths_stack;
thread_local HashMap<int, HashMap<String, Ref<Resource>>> ResourceLoader::res_ref_overrides;
+SafeBinaryMutex<ResourceLoader::BINARY_MUTEX_TAG> &_get_res_loader_mutex() {
+ return ResourceLoader::thread_load_mutex;
+}
+
template <>
-thread_local uint32_t SafeBinaryMutex<ResourceLoader::BINARY_MUTEX_TAG>::count = 0;
+thread_local SafeBinaryMutex<ResourceLoader::BINARY_MUTEX_TAG>::TLSData SafeBinaryMutex<ResourceLoader::BINARY_MUTEX_TAG>::tls_data(_get_res_loader_mutex());
SafeBinaryMutex<ResourceLoader::BINARY_MUTEX_TAG> ResourceLoader::thread_load_mutex;
HashMap<String, ResourceLoader::ThreadLoadTask> ResourceLoader::thread_load_tasks;
bool ResourceLoader::cleaning_tasks = false;
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index ec9997891e..f75bf019fb 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -100,6 +100,8 @@ typedef Error (*ResourceLoaderImport)(const String &p_path);
typedef void (*ResourceLoadedCallback)(Ref<Resource> p_resource, const String &p_path);
class ResourceLoader {
+ friend class LoadToken;
+
enum {
MAX_LOADERS = 64
};
@@ -121,6 +123,7 @@ public:
struct LoadToken : public RefCounted {
String local_path;
String user_path;
+ uint32_t user_rc = 0; // Having user RC implies regular RC incremented in one, until the user RC reaches zero.
Ref<Resource> res_if_unregistered;
void clear();
@@ -130,10 +133,13 @@ public:
static const int BINARY_MUTEX_TAG = 1;
- static Ref<LoadToken> _load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode);
+ static Ref<LoadToken> _load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode, bool p_for_user = false);
static Ref<Resource> _load_complete(LoadToken &p_load_token, Error *r_error);
private:
+ static LoadToken *_load_threaded_request_reuse_user_token(const String &p_path);
+ static void _load_threaded_request_setup_user_token(LoadToken *p_token, const String &p_path);
+
static Ref<Resource> _load_complete_inner(LoadToken &p_load_token, Error *r_error, MutexLock<SafeBinaryMutex<BINARY_MUTEX_TAG>> &p_thread_load_lock);
static Ref<ResourceFormatLoader> loader[MAX_LOADERS];
@@ -171,7 +177,6 @@ private:
bool need_wait = true;
LoadToken *load_token = nullptr;
String local_path;
- String remapped_path;
String type_hint;
float progress = 0.0f;
float max_reported_progress = 0.0f;
@@ -180,18 +185,19 @@ private:
ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE;
Error error = OK;
Ref<Resource> resource;
- bool xl_remapped = false;
bool use_sub_threads = false;
HashSet<String> sub_tasks;
};
- static void _thread_load_function(void *p_userdata);
+ static void _run_load_task(void *p_userdata);
static thread_local int load_nesting;
- static thread_local WorkerThreadPool::TaskID caller_task_id;
static thread_local HashMap<int, HashMap<String, Ref<Resource>>> res_ref_overrides; // Outermost key is nesting level.
static thread_local Vector<String> load_paths_stack;
+
static SafeBinaryMutex<BINARY_MUTEX_TAG> thread_load_mutex;
+ friend SafeBinaryMutex<BINARY_MUTEX_TAG> &_get_res_loader_mutex();
+
static HashMap<String, ThreadLoadTask> thread_load_tasks;
static bool cleaning_tasks;
diff --git a/core/io/stream_peer_tls.cpp b/core/io/stream_peer_tls.cpp
index 69877974e6..f04e217a26 100644
--- a/core/io/stream_peer_tls.cpp
+++ b/core/io/stream_peer_tls.cpp
@@ -32,11 +32,11 @@
#include "core/config/engine.h"
-StreamPeerTLS *(*StreamPeerTLS::_create)() = nullptr;
+StreamPeerTLS *(*StreamPeerTLS::_create)(bool p_notify_postinitialize) = nullptr;
-StreamPeerTLS *StreamPeerTLS::create() {
+StreamPeerTLS *StreamPeerTLS::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
return nullptr;
}
diff --git a/core/io/stream_peer_tls.h b/core/io/stream_peer_tls.h
index 5894abb7a4..3e03e25a2d 100644
--- a/core/io/stream_peer_tls.h
+++ b/core/io/stream_peer_tls.h
@@ -38,7 +38,7 @@ class StreamPeerTLS : public StreamPeer {
GDCLASS(StreamPeerTLS, StreamPeer);
protected:
- static StreamPeerTLS *(*_create)();
+ static StreamPeerTLS *(*_create)(bool p_notify_postinitialize);
static void _bind_methods();
public:
@@ -58,7 +58,7 @@ public:
virtual void disconnect_from_stream() = 0;
- static StreamPeerTLS *create();
+ static StreamPeerTLS *create(bool p_notify_postinitialize = true);
static bool is_available();
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index 7d58f7a724..5c793a676f 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -181,7 +181,7 @@ public:
return 0;
}
- static GDExtensionObjectPtr placeholder_class_create_instance(void *p_class_userdata) {
+ static GDExtensionObjectPtr placeholder_class_create_instance(void *p_class_userdata, bool p_notify_postinitialize) {
ClassDB::ClassInfo *ti = (ClassDB::ClassInfo *)p_class_userdata;
// Find the closest native parent, that isn't a runtime class.
@@ -192,7 +192,7 @@ public:
ERR_FAIL_NULL_V(native_parent->creation_func, nullptr);
// Construct a placeholder.
- Object *obj = native_parent->creation_func();
+ Object *obj = native_parent->creation_func(p_notify_postinitialize);
// ClassDB::set_object_extension_instance() won't be called for placeholders.
// We need need to make sure that all the things it would have done (even if
@@ -525,12 +525,12 @@ StringName ClassDB::get_compatibility_class(const StringName &p_class) {
return StringName();
}
-Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require_real_class) {
+Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require_real_class, bool p_notify_postinitialize) {
ClassInfo *ti;
{
OBJTYPE_RLOCK;
ti = classes.getptr(p_class);
- if (!ti || ti->disabled || !ti->creation_func || (ti->gdextension && !ti->gdextension->create_instance)) {
+ if (!_can_instantiate(ti)) {
if (compat_classes.has(p_class)) {
ti = classes.getptr(compat_classes[p_class]);
}
@@ -539,36 +539,80 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require
ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled.");
ERR_FAIL_NULL_V_MSG(ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated.");
}
+
#ifdef TOOLS_ENABLED
if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
ERR_PRINT("Class '" + String(p_class) + "' can only be instantiated by editor.");
return nullptr;
}
#endif
- if (ti->gdextension && ti->gdextension->create_instance) {
- ObjectGDExtension *extension = ti->gdextension;
-#ifdef TOOLS_ENABLED
- if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) {
- extension = get_placeholder_extension(ti->name);
- }
-#endif
- return (Object *)extension->create_instance(extension->class_userdata);
- } else {
+
#ifdef TOOLS_ENABLED
- if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) {
- if (!ti->inherits_ptr || !ti->inherits_ptr->creation_func) {
- ERR_PRINT(vformat("Cannot make a placeholder instance of runtime class %s because its parent cannot be constructed.", ti->name));
- } else {
- ObjectGDExtension *extension = get_placeholder_extension(ti->name);
- return (Object *)extension->create_instance(extension->class_userdata);
+ // Try to create placeholder.
+ 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) {
+ can_create_placeholder = true;
+ }
+#ifndef DISABLE_DEPRECATED
+ else if (ti->gdextension->create_instance) {
+ can_create_placeholder = true;
}
+#endif // DISABLE_DEPRECATED
+ } else if (!ti->inherits_ptr || !ti->inherits_ptr->creation_func) {
+ ERR_PRINT(vformat("Cannot make a placeholder instance of runtime class %s because its parent cannot be constructed.", ti->name));
+ } else {
+ can_create_placeholder = true;
}
-#endif
- return ti->creation_func();
+ if (can_create_placeholder) {
+ ObjectGDExtension *extension = get_placeholder_extension(ti->name);
+ return (Object *)extension->create_instance2(extension->class_userdata, p_notify_postinitialize);
+ }
+ }
+#endif // TOOLS_ENABLED
+
+ if (ti->gdextension && ti->gdextension->create_instance2) {
+ ObjectGDExtension *extension = ti->gdextension;
+ return (Object *)extension->create_instance2(extension->class_userdata, p_notify_postinitialize);
+ }
+#ifndef DISABLE_DEPRECATED
+ 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);
}
}
+bool ClassDB::_can_instantiate(ClassInfo *p_class_info) {
+ if (!p_class_info) {
+ return false;
+ }
+
+ if (p_class_info->disabled || !p_class_info->creation_func) {
+ return false;
+ }
+
+ if (!p_class_info->gdextension) {
+ return true;
+ }
+
+ if (p_class_info->gdextension->create_instance2) {
+ return true;
+ }
+
+#ifndef DISABLE_DEPRECATED
+ if (p_class_info->gdextension->create_instance) {
+ return true;
+ }
+#endif // DISABLE_DEPRECATED
+ return false;
+}
+
Object *ClassDB::instantiate(const StringName &p_class) {
return _instantiate_internal(p_class);
}
@@ -577,6 +621,10 @@ Object *ClassDB::instantiate_no_placeholders(const StringName &p_class) {
return _instantiate_internal(p_class, true);
}
+Object *ClassDB::instantiate_without_postinitialization(const StringName &p_class) {
+ return _instantiate_internal(p_class, true, false);
+}
+
#ifdef TOOLS_ENABLED
ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class) {
ObjectGDExtension *placeholder_extension = placeholder_extensions.getptr(p_class);
@@ -588,7 +636,7 @@ ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class)
{
OBJTYPE_RLOCK;
ti = classes.getptr(p_class);
- if (!ti || ti->disabled || !ti->creation_func || (ti->gdextension && !ti->gdextension->create_instance)) {
+ if (!_can_instantiate(ti)) {
if (compat_classes.has(p_class)) {
ti = classes.getptr(compat_classes[p_class]);
}
@@ -649,7 +697,10 @@ ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class)
placeholder_extension->get_rid = &PlaceholderExtensionInstance::placeholder_instance_get_rid;
placeholder_extension->class_userdata = ti;
- placeholder_extension->create_instance = &PlaceholderExtensionInstance::placeholder_class_create_instance;
+#ifndef DISABLE_DEPRECATED
+ placeholder_extension->create_instance = nullptr;
+#endif // DISABLE_DEPRECATED
+ placeholder_extension->create_instance2 = &PlaceholderExtensionInstance::placeholder_class_create_instance;
placeholder_extension->free_instance = &PlaceholderExtensionInstance::placeholder_class_free_instance;
placeholder_extension->get_virtual = &PlaceholderExtensionInstance::placeholder_class_get_virtual;
placeholder_extension->get_virtual_call_data = nullptr;
@@ -666,7 +717,7 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName &
{
OBJTYPE_RLOCK;
ti = classes.getptr(p_class);
- if (!ti || ti->disabled || !ti->creation_func || (ti->gdextension && !ti->gdextension->create_instance)) {
+ if (!_can_instantiate(ti)) {
if (compat_classes.has(p_class)) {
ti = classes.getptr(compat_classes[p_class]);
}
@@ -703,7 +754,7 @@ bool ClassDB::can_instantiate(const StringName &p_class) {
return false;
}
#endif
- return (!ti->disabled && ti->creation_func != nullptr && !(ti->gdextension && !ti->gdextension->create_instance));
+ return _can_instantiate(ti);
}
bool ClassDB::is_abstract(const StringName &p_class) {
@@ -718,7 +769,18 @@ bool ClassDB::is_abstract(const StringName &p_class) {
Ref<Script> scr = ResourceLoader::load(path);
return scr.is_valid() && scr->is_valid() && scr->is_abstract();
}
- return ti->creation_func == nullptr && (!ti->gdextension || ti->gdextension->create_instance == nullptr);
+
+ if (ti->creation_func != nullptr) {
+ return false;
+ }
+ if (!ti->gdextension) {
+ return true;
+ }
+#ifndef DISABLE_DEPRECATED
+ return ti->gdextension->create_instance2 == nullptr && ti->gdextension->create_instance == nullptr;
+#else
+ return ti->gdextension->create_instance2 == nullptr;
+#endif // DISABLE_DEPRECATED
}
bool ClassDB::is_virtual(const StringName &p_class) {
@@ -738,7 +800,7 @@ bool ClassDB::is_virtual(const StringName &p_class) {
return false;
}
#endif
- return (!ti->disabled && ti->creation_func != nullptr && !(ti->gdextension && !ti->gdextension->create_instance) && ti->is_virtual);
+ return (_can_instantiate(ti) && ti->is_virtual);
}
void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) {
diff --git a/core/object/class_db.h b/core/object/class_db.h
index d83feafeee..d6a95b58e2 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -134,15 +134,21 @@ public:
bool reloadable = false;
bool is_virtual = false;
bool is_runtime = false;
- Object *(*creation_func)() = nullptr;
+ // The bool argument indicates the need to postinitialize.
+ Object *(*creation_func)(bool) = nullptr;
ClassInfo() {}
~ClassInfo() {}
};
template <typename T>
- static Object *creator() {
- return memnew(T);
+ static Object *creator(bool p_notify_postinitialize) {
+ Object *ret = new ("") T;
+ ret->_initialize();
+ if (p_notify_postinitialize) {
+ ret->_postinitialize();
+ }
+ return ret;
}
static RWLock lock;
@@ -183,7 +189,9 @@ private:
static MethodBind *_bind_vararg_method(MethodBind *p_bind, const StringName &p_name, const Vector<Variant> &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);
+ static Object *_instantiate_internal(const StringName &p_class, bool p_require_real_class = false, bool p_notify_postinitialize = true);
+
+ static bool _can_instantiate(ClassInfo *p_class_info);
public:
// DO NOT USE THIS!!!!!! NEEDS TO BE PUBLIC BUT DO NOT USE NO MATTER WHAT!!!
@@ -256,8 +264,8 @@ public:
static void unregister_extension_class(const StringName &p_class, bool p_free_method_binds = true);
template <typename T>
- static Object *_create_ptr_func() {
- return T::create();
+ static Object *_create_ptr_func(bool p_notify_postinitialize) {
+ return T::create(p_notify_postinitialize);
}
template <typename T>
@@ -292,6 +300,7 @@ public:
static bool is_virtual(const StringName &p_class);
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 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.cpp b/core/object/object.cpp
index a2926a478d..161b1e3dbe 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -207,10 +207,13 @@ void Object::cancel_free() {
_predelete_ok = false;
}
-void Object::_postinitialize() {
- _class_name_ptr = _get_class_namev(); // Set the direct pointer, which is much faster to obtain, but can only happen after postinitialize.
+void Object::_initialize() {
+ _class_name_ptr = _get_class_namev(); // Set the direct pointer, which is much faster to obtain, but can only happen after _initialize.
_initialize_classv();
_class_name_ptr = nullptr; // May have been called from a constructor.
+}
+
+void Object::_postinitialize() {
notification(NOTIFICATION_POSTINITIALIZE);
}
@@ -2129,6 +2132,7 @@ bool predelete_handler(Object *p_object) {
}
void postinitialize_handler(Object *p_object) {
+ p_object->_initialize();
p_object->_postinitialize();
}
diff --git a/core/object/object.h b/core/object/object.h
index adb50268d2..7307b7ede0 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -350,7 +350,10 @@ struct ObjectGDExtension {
}
void *class_userdata = nullptr;
+#ifndef DISABLE_DEPRECATED
GDExtensionClassCreateInstance create_instance;
+#endif // DISABLE_DEPRECATED
+ GDExtensionClassCreateInstance2 create_instance2;
GDExtensionClassFreeInstance free_instance;
GDExtensionClassGetVirtual get_virtual;
GDExtensionClassGetVirtualCallData get_virtual_call_data;
@@ -632,6 +635,7 @@ private:
int _predelete_ok = 0;
ObjectID _instance_id;
bool _predelete();
+ void _initialize();
void _postinitialize();
bool _can_translate = true;
bool _emitting = false;
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index 56b9fa8475..7fd43c4094 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/core/object/worker_thread_pool.cpp
@@ -32,6 +32,7 @@
#include "core/object/script_language.h"
#include "core/os/os.h"
+#include "core/os/safe_binary_mutex.h"
#include "core/os/thread_safe.h"
WorkerThreadPool::Task *const WorkerThreadPool::ThreadData::YIELDING = (Task *)1;
@@ -46,7 +47,7 @@ void WorkerThreadPool::Task::free_template_userdata() {
WorkerThreadPool *WorkerThreadPool::singleton = nullptr;
#ifdef THREADS_ENABLED
-thread_local uintptr_t WorkerThreadPool::unlockable_mutexes[MAX_UNLOCKABLE_MUTEXES] = {};
+thread_local WorkerThreadPool::UnlockableLocks WorkerThreadPool::unlockable_locks[MAX_UNLOCKABLE_LOCKS];
#endif
void WorkerThreadPool::_process_task(Task *p_task) {
@@ -428,13 +429,9 @@ Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
void WorkerThreadPool::_lock_unlockable_mutexes() {
#ifdef THREADS_ENABLED
- for (uint32_t i = 0; i < MAX_UNLOCKABLE_MUTEXES; i++) {
- if (unlockable_mutexes[i]) {
- if ((((uintptr_t)unlockable_mutexes[i]) & 1) == 0) {
- ((Mutex *)unlockable_mutexes[i])->lock();
- } else {
- ((BinaryMutex *)(unlockable_mutexes[i] & ~1))->lock();
- }
+ for (uint32_t i = 0; i < MAX_UNLOCKABLE_LOCKS; i++) {
+ if (unlockable_locks[i].ulock) {
+ unlockable_locks[i].ulock->lock();
}
}
#endif
@@ -442,13 +439,9 @@ void WorkerThreadPool::_lock_unlockable_mutexes() {
void WorkerThreadPool::_unlock_unlockable_mutexes() {
#ifdef THREADS_ENABLED
- for (uint32_t i = 0; i < MAX_UNLOCKABLE_MUTEXES; i++) {
- if (unlockable_mutexes[i]) {
- if ((((uintptr_t)unlockable_mutexes[i]) & 1) == 0) {
- ((Mutex *)unlockable_mutexes[i])->unlock();
- } else {
- ((BinaryMutex *)(unlockable_mutexes[i] & ~1))->unlock();
- }
+ for (uint32_t i = 0; i < MAX_UNLOCKABLE_LOCKS; i++) {
+ if (unlockable_locks[i].ulock) {
+ unlockable_locks[i].ulock->unlock();
}
}
#endif
@@ -665,38 +658,38 @@ int WorkerThreadPool::get_thread_index() {
return singleton->thread_ids.has(tid) ? singleton->thread_ids[tid] : -1;
}
-#ifdef THREADS_ENABLED
-uint32_t WorkerThreadPool::thread_enter_unlock_allowance_zone(Mutex *p_mutex) {
- return _thread_enter_unlock_allowance_zone(p_mutex, false);
-}
-
-uint32_t WorkerThreadPool::thread_enter_unlock_allowance_zone(BinaryMutex *p_mutex) {
- return _thread_enter_unlock_allowance_zone(p_mutex, true);
+WorkerThreadPool::TaskID WorkerThreadPool::get_caller_task_id() {
+ int th_index = get_thread_index();
+ if (th_index != -1 && singleton->threads[th_index].current_task) {
+ return singleton->threads[th_index].current_task->self;
+ } else {
+ return INVALID_TASK_ID;
+ }
}
-uint32_t WorkerThreadPool::_thread_enter_unlock_allowance_zone(void *p_mutex, bool p_is_binary) {
- for (uint32_t i = 0; i < MAX_UNLOCKABLE_MUTEXES; i++) {
- if (unlikely((unlockable_mutexes[i] & ~1) == (uintptr_t)p_mutex)) {
+#ifdef THREADS_ENABLED
+uint32_t WorkerThreadPool::_thread_enter_unlock_allowance_zone(THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &p_ulock) {
+ for (uint32_t i = 0; i < MAX_UNLOCKABLE_LOCKS; i++) {
+ DEV_ASSERT((bool)unlockable_locks[i].ulock == (bool)unlockable_locks[i].rc);
+ if (unlockable_locks[i].ulock == &p_ulock) {
// Already registered in the current thread.
- return UINT32_MAX;
- }
- if (!unlockable_mutexes[i]) {
- unlockable_mutexes[i] = (uintptr_t)p_mutex;
- if (p_is_binary) {
- unlockable_mutexes[i] |= 1;
- }
+ unlockable_locks[i].rc++;
+ return i;
+ } else if (!unlockable_locks[i].ulock) {
+ unlockable_locks[i].ulock = &p_ulock;
+ unlockable_locks[i].rc = 1;
return i;
}
}
- ERR_FAIL_V_MSG(UINT32_MAX, "No more unlockable mutex slots available. Engine bug.");
+ ERR_FAIL_V_MSG(UINT32_MAX, "No more unlockable lock slots available. Engine bug.");
}
void WorkerThreadPool::thread_exit_unlock_allowance_zone(uint32_t p_zone_id) {
- if (p_zone_id == UINT32_MAX) {
- return;
+ DEV_ASSERT(unlockable_locks[p_zone_id].ulock && unlockable_locks[p_zone_id].rc);
+ unlockable_locks[p_zone_id].rc--;
+ if (unlockable_locks[p_zone_id].rc == 0) {
+ unlockable_locks[p_zone_id].ulock = nullptr;
}
- DEV_ASSERT(unlockable_mutexes[p_zone_id]);
- unlockable_mutexes[p_zone_id] = 0;
}
#endif
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
index 8774143abf..5be4f20927 100644
--- a/core/object/worker_thread_pool.h
+++ b/core/object/worker_thread_pool.h
@@ -162,8 +162,12 @@ private:
static WorkerThreadPool *singleton;
#ifdef THREADS_ENABLED
- static const uint32_t MAX_UNLOCKABLE_MUTEXES = 2;
- static thread_local uintptr_t unlockable_mutexes[MAX_UNLOCKABLE_MUTEXES];
+ static const uint32_t MAX_UNLOCKABLE_LOCKS = 2;
+ struct UnlockableLocks {
+ THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> *ulock = nullptr;
+ uint32_t rc = 0;
+ };
+ static thread_local UnlockableLocks unlockable_locks[MAX_UNLOCKABLE_LOCKS];
#endif
TaskID _add_task(const Callable &p_callable, void (*p_func)(void *), void *p_userdata, BaseTemplateUserdata *p_template_userdata, bool p_high_priority, const String &p_description);
@@ -192,7 +196,7 @@ private:
void _wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task);
#ifdef THREADS_ENABLED
- static uint32_t _thread_enter_unlock_allowance_zone(void *p_mutex, bool p_is_binary);
+ static uint32_t _thread_enter_unlock_allowance_zone(THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &p_ulock);
#endif
void _lock_unlockable_mutexes();
@@ -239,13 +243,17 @@ public:
static WorkerThreadPool *get_singleton() { return singleton; }
static int get_thread_index();
+ static TaskID get_caller_task_id();
#ifdef THREADS_ENABLED
- static uint32_t thread_enter_unlock_allowance_zone(Mutex *p_mutex);
- static uint32_t thread_enter_unlock_allowance_zone(BinaryMutex *p_mutex);
+ _ALWAYS_INLINE_ static uint32_t thread_enter_unlock_allowance_zone(const MutexLock<BinaryMutex> &p_lock) { return _thread_enter_unlock_allowance_zone(p_lock._get_lock()); }
+ template <int Tag>
+ _ALWAYS_INLINE_ static uint32_t thread_enter_unlock_allowance_zone(const SafeBinaryMutex<Tag> &p_mutex) { return _thread_enter_unlock_allowance_zone(p_mutex._get_lock()); }
static void thread_exit_unlock_allowance_zone(uint32_t p_zone_id);
#else
- static uint32_t thread_enter_unlock_allowance_zone(void *p_mutex) { return UINT32_MAX; }
+ static uint32_t thread_enter_unlock_allowance_zone(const MutexLock<BinaryMutex> &p_lock) { return UINT32_MAX; }
+ template <int Tag>
+ static uint32_t thread_enter_unlock_allowance_zone(const SafeBinaryMutex<Tag> &p_mutex) { return UINT32_MAX; }
static void thread_exit_unlock_allowance_zone(uint32_t p_zone_id) {}
#endif
diff --git a/core/os/condition_variable.h b/core/os/condition_variable.h
index fa1355e98c..c819fa6b40 100644
--- a/core/os/condition_variable.h
+++ b/core/os/condition_variable.h
@@ -32,6 +32,7 @@
#define CONDITION_VARIABLE_H
#include "core/os/mutex.h"
+#include "core/os/safe_binary_mutex.h"
#ifdef THREADS_ENABLED
@@ -56,7 +57,12 @@ class ConditionVariable {
public:
template <typename BinaryMutexT>
_ALWAYS_INLINE_ void wait(const MutexLock<BinaryMutexT> &p_lock) const {
- condition.wait(const_cast<THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &>(p_lock.lock));
+ condition.wait(const_cast<THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &>(p_lock._get_lock()));
+ }
+
+ template <int Tag>
+ _ALWAYS_INLINE_ void wait(const MutexLock<SafeBinaryMutex<Tag>> &p_lock) const {
+ condition.wait(const_cast<THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &>(p_lock.mutex._get_lock()));
}
_ALWAYS_INLINE_ void notify_one() const {
diff --git a/core/os/mutex.h b/core/os/mutex.h
index 3e7aa81bc1..a968fd7029 100644
--- a/core/os/mutex.h
+++ b/core/os/mutex.h
@@ -72,13 +72,28 @@ public:
template <typename MutexT>
class MutexLock {
- friend class ConditionVariable;
-
- THREADING_NAMESPACE::unique_lock<typename MutexT::StdMutexType> lock;
+ mutable THREADING_NAMESPACE::unique_lock<typename MutexT::StdMutexType> lock;
public:
explicit MutexLock(const MutexT &p_mutex) :
lock(p_mutex.mutex) {}
+
+ // Clarification: all the funny syntax is needed so this function exists only for binary mutexes.
+ template <typename T = MutexT>
+ _ALWAYS_INLINE_ THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &_get_lock(
+ typename std::enable_if<std::is_same<T, THREADING_NAMESPACE::mutex>::value> * = nullptr) const {
+ return lock;
+ }
+
+ _ALWAYS_INLINE_ void temp_relock() const {
+ lock.lock();
+ }
+
+ _ALWAYS_INLINE_ void temp_unlock() const {
+ lock.unlock();
+ }
+
+ // TODO: Implement a `try_temp_relock` if needed (will also need a dummy method below).
};
using Mutex = MutexImpl<THREADING_NAMESPACE::recursive_mutex>; // Recursive, for general use
@@ -104,6 +119,9 @@ template <typename MutexT>
class MutexLock {
public:
MutexLock(const MutexT &p_mutex) {}
+
+ void temp_relock() const {}
+ void temp_unlock() const {}
};
using Mutex = MutexImpl;
diff --git a/core/os/safe_binary_mutex.h b/core/os/safe_binary_mutex.h
index 1e98cc074c..74a20043a3 100644
--- a/core/os/safe_binary_mutex.h
+++ b/core/os/safe_binary_mutex.h
@@ -37,6 +37,11 @@
#ifdef THREADS_ENABLED
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundefined-var-template"
+#endif
+
// A very special kind of mutex, used in scenarios where these
// requirements hold at the same time:
// - Must be used with a condition variable (only binary mutexes are suitable).
@@ -47,69 +52,90 @@
// Also, don't forget to declare the thread_local variable on each use.
template <int Tag>
class SafeBinaryMutex {
- friend class MutexLock<SafeBinaryMutex>;
+ friend class MutexLock<SafeBinaryMutex<Tag>>;
using StdMutexType = THREADING_NAMESPACE::mutex;
mutable THREADING_NAMESPACE::mutex mutex;
- static thread_local uint32_t count;
+
+ struct TLSData {
+ mutable THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> lock;
+ uint32_t count = 0;
+
+ TLSData(SafeBinaryMutex<Tag> &p_mutex) :
+ lock(p_mutex.mutex, THREADING_NAMESPACE::defer_lock) {}
+ };
+ static thread_local TLSData tls_data;
public:
_ALWAYS_INLINE_ void lock() const {
- if (++count == 1) {
- mutex.lock();
+ if (++tls_data.count == 1) {
+ tls_data.lock.lock();
}
}
_ALWAYS_INLINE_ void unlock() const {
- DEV_ASSERT(count);
- if (--count == 0) {
- mutex.unlock();
+ DEV_ASSERT(tls_data.count);
+ if (--tls_data.count == 0) {
+ tls_data.lock.unlock();
}
}
- _ALWAYS_INLINE_ bool try_lock() const {
- if (count) {
- count++;
- return true;
- } else {
- if (mutex.try_lock()) {
- count++;
- return true;
- } else {
- return false;
- }
- }
+ _ALWAYS_INLINE_ THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &_get_lock() const {
+ return const_cast<THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &>(tls_data.lock);
+ }
+
+ _ALWAYS_INLINE_ SafeBinaryMutex() {
}
- ~SafeBinaryMutex() {
- DEV_ASSERT(!count);
+ _ALWAYS_INLINE_ ~SafeBinaryMutex() {
+ DEV_ASSERT(!tls_data.count);
}
};
-// This specialization is needed so manual locking and MutexLock can be used
-// at the same time on a SafeBinaryMutex.
template <int Tag>
class MutexLock<SafeBinaryMutex<Tag>> {
friend class ConditionVariable;
- THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> lock;
+ const SafeBinaryMutex<Tag> &mutex;
public:
- _ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) :
- lock(p_mutex.mutex) {
- SafeBinaryMutex<Tag>::count++;
- };
- _ALWAYS_INLINE_ ~MutexLock() {
- SafeBinaryMutex<Tag>::count--;
- };
+ explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) :
+ mutex(p_mutex) {
+ mutex.lock();
+ }
+
+ ~MutexLock() {
+ mutex.unlock();
+ }
+
+ _ALWAYS_INLINE_ void temp_relock() const {
+ mutex.lock();
+ }
+
+ _ALWAYS_INLINE_ void temp_unlock() const {
+ mutex.unlock();
+ }
+
+ // TODO: Implement a `try_temp_relock` if needed (will also need a dummy method below).
};
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
#else // No threads.
template <int Tag>
-class SafeBinaryMutex : public MutexImpl {
- static thread_local uint32_t count;
+class SafeBinaryMutex {
+ struct TLSData {
+ TLSData(SafeBinaryMutex<Tag> &p_mutex) {}
+ };
+ static thread_local TLSData tls_data;
+
+public:
+ void lock() const {}
+ void unlock() const {}
};
template <int Tag>
@@ -117,6 +143,9 @@ class MutexLock<SafeBinaryMutex<Tag>> {
public:
MutexLock(const SafeBinaryMutex<Tag> &p_mutex) {}
~MutexLock() {}
+
+ void temp_relock() const {}
+ void temp_unlock() const {}
};
#endif // THREADS_ENABLED
diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp
index 8ae2efb787..fdc72bc8dc 100644
--- a/core/string/node_path.cpp
+++ b/core/string/node_path.cpp
@@ -215,7 +215,10 @@ StringName NodePath::get_concatenated_names() const {
String concatenated;
const StringName *sn = data->path.ptr();
for (int i = 0; i < pc; i++) {
- concatenated += i == 0 ? sn[i].operator String() : "/" + sn[i];
+ if (i > 0) {
+ concatenated += "/";
+ }
+ concatenated += sn[i].operator String();
}
data->concatenated_path = concatenated;
}
@@ -230,7 +233,10 @@ StringName NodePath::get_concatenated_subnames() const {
String concatenated;
const StringName *ssn = data->subpath.ptr();
for (int i = 0; i < spc; i++) {
- concatenated += i == 0 ? ssn[i].operator String() : ":" + ssn[i];
+ if (i > 0) {
+ concatenated += ":";
+ }
+ concatenated += ssn[i].operator String();
}
data->concatenated_subpath = concatenated;
}
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 2cfc48d395..303998ab50 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -1694,30 +1694,40 @@ char32_t String::char_lowercase(char32_t p_char) {
}
String String::to_upper() const {
- String upper = *this;
+ if (is_empty()) {
+ return *this;
+ }
- for (int i = 0; i < upper.size(); i++) {
- const char32_t s = upper[i];
- const char32_t t = _find_upper(s);
- if (s != t) { // avoid copy on write
- upper[i] = t;
- }
+ String upper;
+ upper.resize(size());
+ const char32_t *old_ptr = ptr();
+ char32_t *upper_ptrw = upper.ptrw();
+
+ while (*old_ptr) {
+ *upper_ptrw++ = _find_upper(*old_ptr++);
}
+ *upper_ptrw = 0;
+
return upper;
}
String String::to_lower() const {
- String lower = *this;
+ if (is_empty()) {
+ return *this;
+ }
- for (int i = 0; i < lower.size(); i++) {
- const char32_t s = lower[i];
- const char32_t t = _find_lower(s);
- if (s != t) { // avoid copy on write
- lower[i] = t;
- }
+ String lower;
+ lower.resize(size());
+ const char32_t *old_ptr = ptr();
+ char32_t *lower_ptrw = lower.ptrw();
+
+ while (*old_ptr) {
+ *lower_ptrw++ = _find_lower(*old_ptr++);
}
+ *lower_ptrw = 0;
+
return lower;
}
@@ -1955,15 +1965,16 @@ String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) {
static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
String ret;
- char v[2] = { 0, 0 };
+ ret.resize(p_len * 2 + 1);
+ char32_t *ret_ptrw = ret.ptrw();
for (int i = 0; i < p_len; i++) {
- v[0] = hex[p_buffer[i] >> 4];
- ret += v;
- v[0] = hex[p_buffer[i] & 0xF];
- ret += v;
+ *ret_ptrw++ = hex[p_buffer[i] >> 4];
+ *ret_ptrw++ = hex[p_buffer[i] & 0xF];
}
+ *ret_ptrw = 0;
+
return ret;
}
@@ -1986,11 +1997,12 @@ Vector<uint8_t> String::hex_decode() const {
Vector<uint8_t> out;
int len = length() / 2;
out.resize(len);
+ uint8_t *out_ptrw = out.ptrw();
for (int i = 0; i < len; i++) {
char32_t c;
HEX_TO_BYTE(first, i * 2);
HEX_TO_BYTE(second, i * 2 + 1);
- out.write[i] = first * 16 + second;
+ out_ptrw[i] = first * 16 + second;
}
return out;
#undef HEX_TO_BYTE
@@ -2011,14 +2023,16 @@ CharString String::ascii(bool p_allow_extended) const {
CharString cs;
cs.resize(size());
+ char *cs_ptrw = cs.ptrw();
+ const char32_t *this_ptr = ptr();
for (int i = 0; i < size(); i++) {
- char32_t c = operator[](i);
+ char32_t c = this_ptr[i];
if ((c <= 0x7f) || (c <= 0xff && p_allow_extended)) {
- cs[i] = c;
+ cs_ptrw[i] = c;
} else {
print_unicode_error(vformat("Invalid unicode codepoint (%x), cannot represent as ASCII/Latin-1", (uint32_t)c));
- cs[i] = 0x20; // ascii doesn't have a replacement character like unicode, 0x1a is sometimes used but is kinda arcane
+ cs_ptrw[i] = 0x20; // ASCII doesn't have a replacement character like unicode, 0x1a is sometimes used but is kinda arcane.
}
}
@@ -3151,8 +3165,9 @@ Vector<uint8_t> String::md5_buffer() const {
Vector<uint8_t> ret;
ret.resize(16);
+ uint8_t *ret_ptrw = ret.ptrw();
for (int i = 0; i < 16; i++) {
- ret.write[i] = hash[i];
+ ret_ptrw[i] = hash[i];
}
return ret;
}
@@ -3164,8 +3179,9 @@ Vector<uint8_t> String::sha1_buffer() const {
Vector<uint8_t> ret;
ret.resize(20);
+ uint8_t *ret_ptrw = ret.ptrw();
for (int i = 0; i < 20; i++) {
- ret.write[i] = hash[i];
+ ret_ptrw[i] = hash[i];
}
return ret;
@@ -3178,8 +3194,9 @@ Vector<uint8_t> String::sha256_buffer() const {
Vector<uint8_t> ret;
ret.resize(32);
+ uint8_t *ret_ptrw = ret.ptrw();
for (int i = 0; i < 32; i++) {
- ret.write[i] = hash[i];
+ ret_ptrw[i] = hash[i];
}
return ret;
}
@@ -3917,8 +3934,9 @@ Vector<String> String::bigrams() const {
return b;
}
b.resize(n_pairs);
+ String *b_ptrw = b.ptrw();
for (int i = 0; i < n_pairs; i++) {
- b.write[i] = substr(i, 2);
+ b_ptrw[i] = substr(i, 2);
}
return b;
}
@@ -4897,8 +4915,9 @@ String String::xml_unescape() const {
return String();
}
str.resize(len + 1);
- _xml_unescape(get_data(), l, str.ptrw());
- str[len] = 0;
+ char32_t *str_ptrw = str.ptrw();
+ _xml_unescape(get_data(), l, str_ptrw);
+ str_ptrw[len] = 0;
return str;
}
diff --git a/core/templates/command_queue_mt.cpp b/core/templates/command_queue_mt.cpp
index ef75a70868..5fa767263f 100644
--- a/core/templates/command_queue_mt.cpp
+++ b/core/templates/command_queue_mt.cpp
@@ -33,14 +33,6 @@
#include "core/config/project_settings.h"
#include "core/os/os.h"
-void CommandQueueMT::lock() {
- mutex.lock();
-}
-
-void CommandQueueMT::unlock() {
- mutex.unlock();
-}
-
CommandQueueMT::CommandQueueMT() {
command_mem.reserve(DEFAULT_COMMAND_MEM_SIZE_KB * 1024);
}
diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h
index 1e6c6e42a9..8ef5dd3064 100644
--- a/core/templates/command_queue_mt.h
+++ b/core/templates/command_queue_mt.h
@@ -362,23 +362,24 @@ class CommandQueueMT {
return;
}
- lock();
+ MutexLock lock(mutex);
- uint32_t allowance_id = WorkerThreadPool::thread_enter_unlock_allowance_zone(&mutex);
while (flush_read_ptr < command_mem.size()) {
uint64_t size = *(uint64_t *)&command_mem[flush_read_ptr];
flush_read_ptr += 8;
CommandBase *cmd = reinterpret_cast<CommandBase *>(&command_mem[flush_read_ptr]);
+ uint32_t allowance_id = WorkerThreadPool::thread_enter_unlock_allowance_zone(lock);
cmd->call();
+ WorkerThreadPool::thread_exit_unlock_allowance_zone(allowance_id);
// Handle potential realloc due to the command and unlock allowance.
cmd = reinterpret_cast<CommandBase *>(&command_mem[flush_read_ptr]);
if (unlikely(cmd->sync)) {
sync_head++;
- unlock(); // Give an opportunity to awaiters right away.
+ lock.~MutexLock(); // Give an opportunity to awaiters right away.
sync_cond_var.notify_all();
- lock();
+ new (&lock) MutexLock(mutex);
// Handle potential realloc happened during unlock.
cmd = reinterpret_cast<CommandBase *>(&command_mem[flush_read_ptr]);
}
@@ -387,14 +388,11 @@ class CommandQueueMT {
flush_read_ptr += size;
}
- WorkerThreadPool::thread_exit_unlock_allowance_zone(allowance_id);
command_mem.clear();
flush_read_ptr = 0;
_prevent_sync_wraparound();
-
- unlock();
}
_FORCE_INLINE_ void _wait_for_sync(MutexLock<BinaryMutex> &p_lock) {
@@ -410,9 +408,6 @@ class CommandQueueMT {
void _no_op() {}
public:
- void lock();
- void unlock();
-
/* NORMAL PUSH COMMANDS */
DECL_PUSH(0)
SPACE_SEP_LIST(DECL_PUSH, 15)
@@ -446,9 +441,8 @@ public:
}
void set_pump_task_id(WorkerThreadPool::TaskID p_task_id) {
- lock();
+ MutexLock lock(mutex);
pump_task_id = p_task_id;
- unlock();
}
CommandQueueMT();
diff --git a/core/templates/paged_allocator.h b/core/templates/paged_allocator.h
index 4854e1b866..0b70fa02f3 100644
--- a/core/templates/paged_allocator.h
+++ b/core/templates/paged_allocator.h
@@ -55,7 +55,7 @@ class PagedAllocator {
public:
template <typename... Args>
T *alloc(Args &&...p_args) {
- if (thread_safe) {
+ if constexpr (thread_safe) {
spin_lock.lock();
}
if (unlikely(allocs_available == 0)) {
@@ -76,7 +76,7 @@ public:
allocs_available--;
T *alloc = available_pool[allocs_available >> page_shift][allocs_available & page_mask];
- if (thread_safe) {
+ if constexpr (thread_safe) {
spin_lock.unlock();
}
memnew_placement(alloc, T(p_args...));
@@ -84,13 +84,13 @@ public:
}
void free(T *p_mem) {
- if (thread_safe) {
+ if constexpr (thread_safe) {
spin_lock.lock();
}
p_mem->~T();
available_pool[allocs_available >> page_shift][allocs_available & page_mask] = p_mem;
allocs_available++;
- if (thread_safe) {
+ if constexpr (thread_safe) {
spin_lock.unlock();
}
}
@@ -120,28 +120,28 @@ private:
public:
void reset(bool p_allow_unfreed = false) {
- if (thread_safe) {
+ if constexpr (thread_safe) {
spin_lock.lock();
}
_reset(p_allow_unfreed);
- if (thread_safe) {
+ if constexpr (thread_safe) {
spin_lock.unlock();
}
}
bool is_configured() const {
- if (thread_safe) {
+ if constexpr (thread_safe) {
spin_lock.lock();
}
bool result = page_size > 0;
- if (thread_safe) {
+ if constexpr (thread_safe) {
spin_lock.unlock();
}
return result;
}
void configure(uint32_t p_page_size) {
- if (thread_safe) {
+ if constexpr (thread_safe) {
spin_lock.lock();
}
ERR_FAIL_COND(page_pool != nullptr); // Safety check.
@@ -149,7 +149,7 @@ public:
page_size = nearest_power_of_2_templated(p_page_size);
page_mask = page_size - 1;
page_shift = get_shift_from_power_of_2(page_size);
- if (thread_safe) {
+ if constexpr (thread_safe) {
spin_lock.unlock();
}
}
@@ -161,7 +161,7 @@ public:
}
~PagedAllocator() {
- if (thread_safe) {
+ if constexpr (thread_safe) {
spin_lock.lock();
}
bool leaked = allocs_available < pages_allocated * page_size;
@@ -172,7 +172,7 @@ public:
} else {
_reset(false);
}
- if (thread_safe) {
+ if constexpr (thread_safe) {
spin_lock.unlock();
}
}
diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h
index 86304d3c73..537413e2ba 100644
--- a/core/templates/rid_owner.h
+++ b/core/templates/rid_owner.h
@@ -82,7 +82,7 @@ class RID_Alloc : public RID_AllocBase {
mutable SpinLock spin_lock;
_FORCE_INLINE_ RID _allocate_rid() {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.lock();
}
@@ -128,7 +128,7 @@ class RID_Alloc : public RID_AllocBase {
alloc_count++;
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
@@ -156,14 +156,14 @@ public:
if (p_rid == RID()) {
return nullptr;
}
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.lock();
}
uint64_t id = p_rid.get_id();
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
if (unlikely(idx >= max_alloc)) {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
return nullptr;
@@ -176,14 +176,14 @@ public:
if (unlikely(p_initialize)) {
if (unlikely(!(validator_chunks[idx_chunk][idx_element] & 0x80000000))) {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
ERR_FAIL_V_MSG(nullptr, "Initializing already initialized RID");
}
if (unlikely((validator_chunks[idx_chunk][idx_element] & 0x7FFFFFFF) != validator)) {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
ERR_FAIL_V_MSG(nullptr, "Attempting to initialize the wrong RID");
@@ -192,7 +192,7 @@ public:
validator_chunks[idx_chunk][idx_element] &= 0x7FFFFFFF; //initialized
} else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
if ((validator_chunks[idx_chunk][idx_element] & 0x80000000) && validator_chunks[idx_chunk][idx_element] != 0xFFFFFFFF) {
@@ -203,7 +203,7 @@ public:
T *ptr = &chunks[idx_chunk][idx_element];
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
@@ -221,14 +221,14 @@ public:
}
_FORCE_INLINE_ bool owns(const RID &p_rid) const {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.lock();
}
uint64_t id = p_rid.get_id();
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
if (unlikely(idx >= max_alloc)) {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
return false;
@@ -241,7 +241,7 @@ public:
bool owned = (validator != 0x7FFFFFFF) && (validator_chunks[idx_chunk][idx_element] & 0x7FFFFFFF) == validator;
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
@@ -249,14 +249,14 @@ public:
}
_FORCE_INLINE_ void free(const RID &p_rid) {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.lock();
}
uint64_t id = p_rid.get_id();
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
if (unlikely(idx >= max_alloc)) {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
ERR_FAIL();
@@ -267,12 +267,12 @@ public:
uint32_t validator = uint32_t(id >> 32);
if (unlikely(validator_chunks[idx_chunk][idx_element] & 0x80000000)) {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
ERR_FAIL_MSG("Attempted to free an uninitialized or invalid RID.");
} else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
ERR_FAIL();
@@ -284,7 +284,7 @@ public:
alloc_count--;
free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk] = idx;
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
}
@@ -293,7 +293,7 @@ public:
return alloc_count;
}
void get_owned_list(List<RID> *p_owned) const {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.lock();
}
for (size_t i = 0; i < max_alloc; i++) {
@@ -302,14 +302,14 @@ public:
p_owned->push_back(_make_from_id((validator << 32) | i));
}
}
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
}
//used for fast iteration in the elements or RIDs
void fill_owned_buffer(RID *p_rid_buffer) const {
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.lock();
}
uint32_t idx = 0;
@@ -320,7 +320,7 @@ public:
idx++;
}
}
- if (THREAD_SAFE) {
+ if constexpr (THREAD_SAFE) {
spin_lock.unlock();
}
}
diff --git a/core/templates/sort_array.h b/core/templates/sort_array.h
index e7eaf8ee81..5bf5b2819d 100644
--- a/core/templates/sort_array.h
+++ b/core/templates/sort_array.h
@@ -174,14 +174,14 @@ public:
while (true) {
while (compare(p_array[p_first], p_pivot)) {
- if (Validate) {
+ if constexpr (Validate) {
ERR_BAD_COMPARE(p_first == unmodified_last - 1);
}
p_first++;
}
p_last--;
while (compare(p_pivot, p_array[p_last])) {
- if (Validate) {
+ if constexpr (Validate) {
ERR_BAD_COMPARE(p_last == unmodified_first);
}
p_last--;
@@ -251,7 +251,7 @@ public:
inline void unguarded_linear_insert(int64_t p_last, T p_value, T *p_array) const {
int64_t next = p_last - 1;
while (compare(p_value, p_array[next])) {
- if (Validate) {
+ if constexpr (Validate) {
ERR_BAD_COMPARE(next == 0);
}
p_array[p_last] = p_array[next];