summaryrefslogtreecommitdiff
path: root/core/object
diff options
context:
space:
mode:
Diffstat (limited to 'core/object')
-rw-r--r--core/object/callable_method_pointer.cpp4
-rw-r--r--core/object/class_db.cpp136
-rw-r--r--core/object/class_db.h38
-rw-r--r--core/object/message_queue.cpp2
-rw-r--r--core/object/method_bind.cpp25
-rw-r--r--core/object/method_bind.h14
-rw-r--r--core/object/object.cpp236
-rw-r--r--core/object/object.h172
-rw-r--r--core/object/script_language.cpp33
-rw-r--r--core/object/script_language.h11
-rw-r--r--core/object/script_language_extension.cpp4
-rw-r--r--core/object/script_language_extension.h49
-rw-r--r--core/object/worker_thread_pool.cpp474
-rw-r--r--core/object/worker_thread_pool.h198
14 files changed, 1034 insertions, 362 deletions
diff --git a/core/object/callable_method_pointer.cpp b/core/object/callable_method_pointer.cpp
index 1bf926cafc..81f8ab6be2 100644
--- a/core/object/callable_method_pointer.cpp
+++ b/core/object/callable_method_pointer.cpp
@@ -85,9 +85,9 @@ void CallableCustomMethodPointerBase::_setup(uint32_t *p_base_ptr, uint32_t p_pt
// Precompute hash.
for (uint32_t i = 0; i < comp_size; i++) {
if (i == 0) {
- h = hash_djb2_one_32(comp_ptr[i]);
+ h = hash_murmur3_one_32(comp_ptr[i]);
} else {
- h = hash_djb2_one_32(comp_ptr[i], h);
+ h = hash_murmur3_one_32(comp_ptr[i], h);
}
}
}
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index f61bd24efd..9790cc44e3 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -164,7 +164,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
OBJTYPE_RLOCK;
#ifdef DEBUG_METHODS_ENABLED
- uint64_t hash = hash_djb2_one_64(HashMapHasherDefault::hash(VERSION_FULL_CONFIG));
+ uint64_t hash = hash_murmur3_one_64(HashMapHasherDefault::hash(VERSION_FULL_CONFIG));
List<StringName> class_list;
ClassDB::get_class_list(&class_list);
@@ -177,8 +177,8 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
if (t->api != p_api || !t->exposed) {
continue;
}
- hash = hash_djb2_one_64(t->name.hash(), hash);
- hash = hash_djb2_one_64(t->inherits.hash(), hash);
+ hash = hash_murmur3_one_64(t->name.hash(), hash);
+ hash = hash_murmur3_one_64(t->inherits.hash(), hash);
{ //methods
@@ -200,27 +200,27 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
for (const StringName &F : snames) {
MethodBind *mb = t->method_map[F];
- hash = hash_djb2_one_64(mb->get_name().hash(), hash);
- hash = hash_djb2_one_64(mb->get_argument_count(), hash);
- hash = hash_djb2_one_64(mb->get_argument_type(-1), hash); //return
+ hash = hash_murmur3_one_64(mb->get_name().hash(), hash);
+ hash = hash_murmur3_one_64(mb->get_argument_count(), hash);
+ hash = hash_murmur3_one_64(mb->get_argument_type(-1), hash); //return
for (int i = 0; i < mb->get_argument_count(); i++) {
const PropertyInfo info = mb->get_argument_info(i);
- hash = hash_djb2_one_64(info.type, hash);
- hash = hash_djb2_one_64(info.name.hash(), hash);
- hash = hash_djb2_one_64(info.hint, hash);
- hash = hash_djb2_one_64(info.hint_string.hash(), hash);
+ hash = hash_murmur3_one_64(info.type, hash);
+ hash = hash_murmur3_one_64(info.name.hash(), hash);
+ hash = hash_murmur3_one_64(info.hint, hash);
+ hash = hash_murmur3_one_64(info.hint_string.hash(), hash);
}
- hash = hash_djb2_one_64(mb->get_default_argument_count(), hash);
+ hash = hash_murmur3_one_64(mb->get_default_argument_count(), hash);
for (int i = 0; i < mb->get_default_argument_count(); i++) {
//hash should not change, i hope for tis
Variant da = mb->get_default_argument(i);
- hash = hash_djb2_one_64(da.hash(), hash);
+ hash = hash_murmur3_one_64(da.hash(), hash);
}
- hash = hash_djb2_one_64(mb->get_hint_flags(), hash);
+ hash = hash_murmur3_one_64(mb->get_hint_flags(), hash);
}
}
@@ -228,15 +228,15 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
List<StringName> snames;
- for (const KeyValue<StringName, int> &F : t->constant_map) {
+ for (const KeyValue<StringName, int64_t> &F : t->constant_map) {
snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
for (const StringName &F : snames) {
- hash = hash_djb2_one_64(F.hash(), hash);
- hash = hash_djb2_one_64(t->constant_map[F], hash);
+ hash = hash_murmur3_one_64(F.hash(), hash);
+ hash = hash_murmur3_one_64(t->constant_map[F], hash);
}
}
@@ -252,9 +252,9 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
for (const StringName &F : snames) {
MethodInfo &mi = t->signal_map[F];
- hash = hash_djb2_one_64(F.hash(), hash);
+ hash = hash_murmur3_one_64(F.hash(), hash);
for (int i = 0; i < mi.arguments.size(); i++) {
- hash = hash_djb2_one_64(mi.arguments[i].type, hash);
+ hash = hash_murmur3_one_64(mi.arguments[i].type, hash);
}
}
}
@@ -273,23 +273,23 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
PropertySetGet *psg = t->property_setget.getptr(F);
ERR_FAIL_COND_V(!psg, 0);
- hash = hash_djb2_one_64(F.hash(), hash);
- hash = hash_djb2_one_64(psg->setter.hash(), hash);
- hash = hash_djb2_one_64(psg->getter.hash(), hash);
+ hash = hash_murmur3_one_64(F.hash(), hash);
+ hash = hash_murmur3_one_64(psg->setter.hash(), hash);
+ hash = hash_murmur3_one_64(psg->getter.hash(), hash);
}
}
//property list
for (const PropertyInfo &F : t->property_list) {
- hash = hash_djb2_one_64(F.name.hash(), hash);
- hash = hash_djb2_one_64(F.type, hash);
- hash = hash_djb2_one_64(F.hint, hash);
- hash = hash_djb2_one_64(F.hint_string.hash(), hash);
- hash = hash_djb2_one_64(F.usage, hash);
+ hash = hash_murmur3_one_64(F.name.hash(), hash);
+ hash = hash_murmur3_one_64(F.type, hash);
+ hash = hash_murmur3_one_64(F.hint, hash);
+ hash = hash_murmur3_one_64(F.hint_string.hash(), hash);
+ hash = hash_murmur3_one_64(F.usage, hash);
}
}
- return hash;
+ return hash_fmix32(hash);
#else
return 0;
#endif
@@ -305,6 +305,13 @@ void ClassDB::add_compatibility_class(const StringName &p_class, const StringNam
compat_classes[p_class] = p_fallback;
}
+StringName ClassDB::get_compatibility_class(const StringName &p_class) {
+ if (compat_classes.has(p_class)) {
+ return compat_classes[p_class];
+ }
+ return StringName();
+}
+
Object *ClassDB::instantiate(const StringName &p_class) {
ClassInfo *ti;
{
@@ -536,7 +543,7 @@ MethodBind *ClassDB::get_method(const StringName &p_class, const StringName &p_n
return nullptr;
}
-void ClassDB::bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int p_constant) {
+void ClassDB::bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant, bool p_is_bitfield) {
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
@@ -555,13 +562,15 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName
enum_name = enum_name.get_slicec('.', 1);
}
- List<StringName> *constants_list = type->enum_map.getptr(enum_name);
+ ClassInfo::EnumInfo *constants_list = type->enum_map.getptr(enum_name);
if (constants_list) {
- constants_list->push_back(p_name);
+ constants_list->constants.push_back(p_name);
+ constants_list->is_bitfield = p_is_bitfield;
} else {
- List<StringName> new_list;
- new_list.push_back(p_name);
+ ClassInfo::EnumInfo new_list;
+ new_list.is_bitfield = p_is_bitfield;
+ new_list.constants.push_back(p_name);
type->enum_map[enum_name] = new_list;
}
}
@@ -583,7 +592,7 @@ void ClassDB::get_integer_constant_list(const StringName &p_class, List<String>
}
#else
- for (const KeyValue<StringName, int> &E : type->constant_map) {
+ for (const KeyValue<StringName, int64_t> &E : type->constant_map) {
p_constants->push_back(E.key);
}
@@ -596,13 +605,13 @@ void ClassDB::get_integer_constant_list(const StringName &p_class, List<String>
}
}
-int ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success) {
+int64_t ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success) {
OBJTYPE_RLOCK;
ClassInfo *type = classes.getptr(p_class);
while (type) {
- int *constant = type->constant_map.getptr(p_name);
+ int64_t *constant = type->constant_map.getptr(p_name);
if (constant) {
if (p_success) {
*p_success = true;
@@ -645,8 +654,8 @@ StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const S
ClassInfo *type = classes.getptr(p_class);
while (type) {
- for (KeyValue<StringName, List<StringName>> &E : type->enum_map) {
- List<StringName> &constants_list = E.value;
+ for (KeyValue<StringName, ClassInfo::EnumInfo> &E : type->enum_map) {
+ List<StringName> &constants_list = E.value.constants;
const List<StringName>::Element *found = constants_list.find(p_name);
if (found) {
return E.key;
@@ -669,7 +678,7 @@ void ClassDB::get_enum_list(const StringName &p_class, List<StringName> *p_enums
ClassInfo *type = classes.getptr(p_class);
while (type) {
- for (KeyValue<StringName, List<StringName>> &E : type->enum_map) {
+ for (KeyValue<StringName, ClassInfo::EnumInfo> &E : type->enum_map) {
p_enums->push_back(E.key);
}
@@ -687,10 +696,10 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_
ClassInfo *type = classes.getptr(p_class);
while (type) {
- const List<StringName> *constants = type->enum_map.getptr(p_enum);
+ const ClassInfo::EnumInfo *constants = type->enum_map.getptr(p_enum);
if (constants) {
- for (const List<StringName>::Element *E = constants->front(); E; E = E->next()) {
+ for (const List<StringName>::Element *E = constants->constants.front(); E; E = E->next()) {
p_constants->push_back(E->get());
}
}
@@ -748,6 +757,25 @@ bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool
return false;
}
+bool ClassDB::is_enum_bitfield(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ if (type->enum_map.has(p_name) && type->enum_map[p_name].is_bitfield) {
+ return true;
+ }
+ if (p_no_inheritance) {
+ return false;
+ }
+
+ type = type->inherits_ptr;
+ }
+
+ return false;
+}
+
void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal) {
OBJTYPE_WLOCK;
@@ -935,8 +963,11 @@ void ClassDB::add_linked_property(const StringName &p_class, const String &p_pro
ERR_FAIL_COND(!type->property_map.has(p_property));
ERR_FAIL_COND(!type->property_map.has(p_linked_property));
- PropertyInfo &pinfo = type->property_map[p_property];
- pinfo.linked_properties.push_back(p_linked_property);
+ if (!type->linked_properties.has(p_property)) {
+ type->linked_properties.insert(p_property, List<StringName>());
+ }
+ type->linked_properties[p_property].push_back(p_linked_property);
+
#endif
}
@@ -964,6 +995,25 @@ void ClassDB::get_property_list(const StringName &p_class, List<PropertyInfo> *p
}
}
+void ClassDB::get_linked_properties_info(const StringName &p_class, const StringName &p_property, List<StringName> *r_properties, bool p_no_inheritance) {
+#ifdef TOOLS_ENABLED
+ ClassInfo *check = classes.getptr(p_class);
+ while (check) {
+ if (!check->linked_properties.has(p_property)) {
+ return;
+ }
+ for (const StringName &E : check->linked_properties[p_property]) {
+ r_properties->push_back(E);
+ }
+
+ if (p_no_inheritance) {
+ break;
+ }
+ check = check->inherits_ptr;
+ }
+#endif
+}
+
bool ClassDB::get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) {
OBJTYPE_RLOCK;
@@ -1066,7 +1116,7 @@ bool ClassDB::get_property(Object *p_object, const StringName &p_property, Varia
return true;
}
- const int *c = check->constant_map.getptr(p_property); //constants count
+ const int64_t *c = check->constant_map.getptr(p_property); //constants count
if (c) {
r_value = *c;
return true;
diff --git a/core/object/class_db.h b/core/object/class_db.h
index 2448a86e33..5fba52e23e 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -103,8 +103,13 @@ public:
ObjectNativeExtension *native_extension = nullptr;
HashMap<StringName, MethodBind *> method_map;
- HashMap<StringName, int> constant_map;
- HashMap<StringName, List<StringName>> enum_map;
+ HashMap<StringName, int64_t> constant_map;
+ struct EnumInfo {
+ List<StringName> constants;
+ bool is_bitfield = false;
+ };
+
+ HashMap<StringName, EnumInfo> enum_map;
HashMap<StringName, MethodInfo> signal_map;
List<PropertyInfo> property_list;
HashMap<StringName, PropertyInfo> property_map;
@@ -115,6 +120,7 @@ public:
List<MethodInfo> virtual_methods;
HashMap<StringName, MethodInfo> virtual_methods_map;
HashMap<StringName, Vector<Error>> method_error_values;
+ HashMap<StringName, List<StringName>> linked_properties;
#endif
HashMap<StringName, PropertySetGet> property_setget;
@@ -307,6 +313,7 @@ public:
static void add_linked_property(const StringName &p_class, const String &p_property, const String &p_linked_property);
static void get_property_list(const StringName &p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr);
static bool get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr);
+ static void get_linked_properties_info(const StringName &p_class, const StringName &p_property, List<StringName> *r_properties, bool p_no_inheritance = false);
static bool set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid = nullptr);
static bool get_property(Object *p_object, const StringName &p_property, Variant &r_value);
static bool has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance = false);
@@ -325,15 +332,17 @@ public:
static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true, const Vector<String> &p_arg_names = Vector<String>(), bool p_object_core = false);
static void get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false);
- static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int p_constant);
+ static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant, bool p_is_bitfield = false);
static void get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance = false);
- static int get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success = nullptr);
+ static int64_t get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success = nullptr);
static bool has_integer_constant(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
static StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
+ static StringName get_integer_constant_bitfield(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
static void get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance = false);
static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false);
static bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
+ static bool is_enum_bitfield(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
static void set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values);
static Vector<Error> get_method_error_return_values(const StringName &p_class, const StringName &p_method);
@@ -350,6 +359,7 @@ public:
static bool is_resource_extension(const StringName &p_extension);
static void add_compatibility_class(const StringName &p_class, const StringName &p_fallback);
+ static StringName get_compatibility_class(const StringName &p_class);
static void set_current_api(APIType p_api);
static APIType get_current_api();
@@ -370,6 +380,9 @@ public:
#define BIND_ENUM_CONSTANT(m_constant) \
::ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant);
+#define BIND_BITFIELD_FLAG(m_constant) \
+ ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, true);
+
_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr) {
}
@@ -401,20 +414,23 @@ _FORCE_INLINE_ Vector<Error> errarray(P... p_args) {
#define BIND_ENUM_CONSTANT(m_constant) \
::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant);
+#define BIND_BITFIELD_FLAG(m_constant) \
+ ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant, true);
+
#define BIND_METHOD_ERR_RETURN_DOC(m_method, ...)
#endif
-#define GDREGISTER_CLASS(m_class) \
- if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
- ::ClassDB::register_class<m_class>(); \
+#define GDREGISTER_CLASS(m_class) \
+ if (m_class::_class_is_enabled) { \
+ ::ClassDB::register_class<m_class>(); \
}
-#define GDREGISTER_VIRTUAL_CLASS(m_class) \
- if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
- ::ClassDB::register_class<m_class>(true); \
+#define GDREGISTER_VIRTUAL_CLASS(m_class) \
+ if (m_class::_class_is_enabled) { \
+ ::ClassDB::register_class<m_class>(true); \
}
#define GDREGISTER_ABSTRACT_CLASS(m_class) \
- if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
+ if (m_class::_class_is_enabled) { \
::ClassDB::register_abstract_class<m_class>(); \
}
diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp
index fa1945cf79..13dc921c9f 100644
--- a/core/object/message_queue.cpp
+++ b/core/object/message_queue.cpp
@@ -226,7 +226,7 @@ void MessageQueue::_call_function(const Callable &p_callable, const Variant *p_a
Callable::CallError ce;
Variant ret;
- p_callable.call(argptrs, p_argcount, ret, ce);
+ p_callable.callp(argptrs, p_argcount, ret, ce);
if (p_show_error && ce.error != Callable::CallError::CALL_OK) {
ERR_PRINT("Error calling deferred method: " + Variant::get_callable_error_text(p_callable, argptrs, p_argcount, ce) + ".");
}
diff --git a/core/object/method_bind.cpp b/core/object/method_bind.cpp
index a208c1a2b2..a4474ea53b 100644
--- a/core/object/method_bind.cpp
+++ b/core/object/method_bind.cpp
@@ -35,32 +35,27 @@
#include "method_bind.h"
uint32_t MethodBind::get_hash() const {
- uint32_t hash = hash_djb2_one_32(has_return() ? 1 : 0);
- hash = hash_djb2_one_32(get_argument_count(), hash);
-
-#ifndef _MSC_VER
-#warning This needs proper class name and argument type for hashing
-#endif
-#if 0
+ uint32_t hash = hash_murmur3_one_32(has_return() ? 1 : 0);
+ hash = hash_murmur3_one_32(get_argument_count(), hash);
for (int i = (has_return() ? -1 : 0); i < get_argument_count(); i++) {
PropertyInfo pi = i == -1 ? get_return_info() : get_argument_info(i);
- hash = hash_djb2_one_32(get_argument_type(i), hash);
+ hash = hash_murmur3_one_32(get_argument_type(i), hash);
if (pi.class_name != StringName()) {
- hash = hash_djb2_one_32(pi.class_name.operator String().hash(), hash);
+ hash = hash_murmur3_one_32(pi.class_name.operator String().hash(), hash);
}
}
-#endif
- hash = hash_djb2_one_32(get_default_argument_count(), hash);
+
+ hash = hash_murmur3_one_32(get_default_argument_count(), hash);
for (int i = 0; i < get_default_argument_count(); i++) {
Variant v = get_default_argument(i);
- hash = hash_djb2_one_32(v.hash(), hash);
+ hash = hash_murmur3_one_32(v.hash(), hash);
}
- hash = hash_djb2_one_32(is_const(), hash);
- hash = hash_djb2_one_32(is_vararg(), hash);
+ hash = hash_murmur3_one_32(is_const(), hash);
+ hash = hash_murmur3_one_32(is_vararg(), hash);
- return hash;
+ return hash_fmix32(hash);
}
PropertyInfo MethodBind::get_argument_info(int p_argument) const {
diff --git a/core/object/method_bind.h b/core/object/method_bind.h
index 2870195911..d60550c899 100644
--- a/core/object/method_bind.h
+++ b/core/object/method_bind.h
@@ -33,20 +33,6 @@
#include "core/variant/binder_common.h"
-enum MethodFlags {
- METHOD_FLAG_NORMAL = 1,
- METHOD_FLAG_EDITOR = 2,
- METHOD_FLAG_NOSCRIPT = 4,
- METHOD_FLAG_CONST = 8,
- METHOD_FLAG_REVERSE = 16, // used for events
- METHOD_FLAG_VIRTUAL = 32,
- METHOD_FLAG_FROM_SCRIPT = 64,
- METHOD_FLAG_VARARG = 128,
- METHOD_FLAG_STATIC = 256,
- METHOD_FLAG_OBJECT_CORE = 512,
- METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL,
-};
-
VARIANT_ENUM_CAST(MethodFlags)
// some helpers
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 9dec417b11..0fcd1c0e40 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -161,167 +161,11 @@ MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) {
return mi;
}
-MethodInfo::MethodInfo() :
- flags(METHOD_FLAG_NORMAL) {}
-
-MethodInfo::MethodInfo(const String &p_name) :
- name(p_name),
- flags(METHOD_FLAG_NORMAL) {
-}
-
-MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1) :
- name(p_name),
- flags(METHOD_FLAG_NORMAL) {
- arguments.push_back(p_param1);
-}
-
-MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2) :
- name(p_name),
- flags(METHOD_FLAG_NORMAL) {
- arguments.push_back(p_param1);
- arguments.push_back(p_param2);
-}
-
-MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3) :
- name(p_name),
- flags(METHOD_FLAG_NORMAL) {
- arguments.push_back(p_param1);
- arguments.push_back(p_param2);
- arguments.push_back(p_param3);
-}
-
-MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4) :
- name(p_name),
- flags(METHOD_FLAG_NORMAL) {
- arguments.push_back(p_param1);
- arguments.push_back(p_param2);
- arguments.push_back(p_param3);
- arguments.push_back(p_param4);
-}
-
-MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5) :
- name(p_name),
- flags(METHOD_FLAG_NORMAL) {
- arguments.push_back(p_param1);
- arguments.push_back(p_param2);
- arguments.push_back(p_param3);
- arguments.push_back(p_param4);
- arguments.push_back(p_param5);
-}
-
-MethodInfo::MethodInfo(Variant::Type ret) :
- flags(METHOD_FLAG_NORMAL) {
- return_val.type = ret;
-}
-
-MethodInfo::MethodInfo(Variant::Type ret, const String &p_name) :
- name(p_name),
- flags(METHOD_FLAG_NORMAL) {
- return_val.type = ret;
-}
-
-MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1) :
- name(p_name),
- flags(METHOD_FLAG_NORMAL) {
- return_val.type = ret;
- arguments.push_back(p_param1);
-}
-
-MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2) :
- name(p_name),
- flags(METHOD_FLAG_NORMAL) {
- return_val.type = ret;
- arguments.push_back(p_param1);
- arguments.push_back(p_param2);
-}
-
-MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3) :
- name(p_name),
- flags(METHOD_FLAG_NORMAL) {
- return_val.type = ret;
- arguments.push_back(p_param1);
- arguments.push_back(p_param2);
- arguments.push_back(p_param3);
-}
-
-MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4) :
- name(p_name),
- flags(METHOD_FLAG_NORMAL) {
- return_val.type = ret;
- arguments.push_back(p_param1);
- arguments.push_back(p_param2);
- arguments.push_back(p_param3);
- arguments.push_back(p_param4);
-}
-
-MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5) :
- name(p_name),
- flags(METHOD_FLAG_NORMAL) {
- return_val.type = ret;
- arguments.push_back(p_param1);
- arguments.push_back(p_param2);
- arguments.push_back(p_param3);
- arguments.push_back(p_param4);
- arguments.push_back(p_param5);
-}
-
-MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name) :
- name(p_name),
- return_val(p_ret),
- flags(METHOD_FLAG_NORMAL) {
-}
-
-MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1) :
- name(p_name),
- return_val(p_ret),
- flags(METHOD_FLAG_NORMAL) {
- arguments.push_back(p_param1);
-}
-
-MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2) :
- name(p_name),
- return_val(p_ret),
- flags(METHOD_FLAG_NORMAL) {
- arguments.push_back(p_param1);
- arguments.push_back(p_param2);
-}
-
-MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3) :
- name(p_name),
- return_val(p_ret),
- flags(METHOD_FLAG_NORMAL) {
- arguments.push_back(p_param1);
- arguments.push_back(p_param2);
- arguments.push_back(p_param3);
-}
-
-MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4) :
- name(p_name),
- return_val(p_ret),
- flags(METHOD_FLAG_NORMAL) {
- arguments.push_back(p_param1);
- arguments.push_back(p_param2);
- arguments.push_back(p_param3);
- arguments.push_back(p_param4);
-}
-
-MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5) :
- name(p_name),
- return_val(p_ret),
- flags(METHOD_FLAG_NORMAL) {
- arguments.push_back(p_param1);
- arguments.push_back(p_param2);
- arguments.push_back(p_param3);
- arguments.push_back(p_param4);
- arguments.push_back(p_param5);
-}
-
Object::Connection::operator Variant() const {
Dictionary d;
d["signal"] = signal;
d["callable"] = callable;
d["flags"] = flags;
- d["binds"] = binds;
return d;
}
@@ -344,9 +188,6 @@ Object::Connection::Connection(const Variant &p_variant) {
if (d.has("flags")) {
flags = d["flags"];
}
- if (d.has("binds")) {
- binds = d["binds"];
- }
}
bool Object::_predelete() {
@@ -631,7 +472,6 @@ Variant Object::get_indexed(const Vector<StringName> &p_names, bool *r_valid) co
void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) const {
if (script_instance && p_reversed) {
- p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
script_instance->get_property_list(p_list);
}
@@ -648,7 +488,7 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
uint32_t pcount;
const GDNativePropertyInfo *pinfo = _extension->get_property_list(_extension_instance, &pcount);
for (uint32_t i = 0; i < pcount; i++) {
- p_list->push_back(PropertyInfo(Variant::Type(pinfo[i].type), pinfo[i].class_name, PropertyHint(pinfo[i].hint), pinfo[i].hint_string, pinfo[i].usage, pinfo[i].class_name));
+ p_list->push_back(PropertyInfo(pinfo[i]));
}
if (_extension->free_property_list) {
_extension->free_property_list(_extension_instance, pinfo);
@@ -662,7 +502,6 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
}
if (script_instance && !p_reversed) {
- p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
script_instance->get_property_list(p_list);
}
@@ -824,6 +663,51 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
+ return ret;
+ case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
+ }
+ }
+ }
+
+ //extension does not need this, because all methods are registered in MethodBind
+
+ MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
+
+ if (method) {
+ ret = method->call(this, p_args, p_argcount, r_error);
+ } else {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ }
+
+ return ret;
+}
+
+Variant Object::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+
+ if (p_method == CoreStringNames::get_singleton()->_free) {
+ // Free is not const, so fail.
+ r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST;
+ return Variant();
+ }
+
+ Variant ret;
+ OBJ_DEBUG_LOCK
+
+ if (script_instance) {
+ ret = script_instance->call_const(p_method, p_args, p_argcount, r_error);
+ //force jumptable
+ switch (r_error.error) {
+ case Callable::CallError::CALL_OK:
+ return ret;
+ case Callable::CallError::CALL_ERROR_INVALID_METHOD:
+ break;
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
+ break;
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
return ret;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
}
@@ -835,6 +719,10 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_
MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
if (method) {
+ if (!method->is_const()) {
+ r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST;
+ return ret;
+ }
ret = method->call(this, p_args, p_argcount, r_error);
} else {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
@@ -1079,8 +967,6 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
OBJ_DEBUG_LOCK
- Vector<const Variant *> bind_mem;
-
Error err = OK;
for (int i = 0; i < ssize; i++) {
@@ -1095,28 +981,13 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
const Variant **args = p_args;
int argc = p_argcount;
- if (c.binds.size()) {
- //handle binds
- bind_mem.resize(p_argcount + c.binds.size());
-
- for (int j = 0; j < p_argcount; j++) {
- bind_mem.write[j] = p_args[j];
- }
- for (int j = 0; j < c.binds.size(); j++) {
- bind_mem.write[p_argcount + j] = &c.binds[j];
- }
-
- args = (const Variant **)bind_mem.ptr();
- argc = bind_mem.size();
- }
-
if (c.flags & CONNECT_DEFERRED) {
MessageQueue::get_singleton()->push_callablep(c.callable, args, argc, true);
} else {
Callable::CallError ce;
_emitting = true;
Variant ret;
- c.callable.call(args, argc, ret, ce);
+ c.callable.callp(args, argc, ret, ce);
_emitting = false;
if (ce.error != Callable::CallError::CALL_OK) {
@@ -1302,7 +1173,7 @@ void Object::get_signals_connected_to_this(List<Connection> *p_connections) cons
}
}
-Error Object::connect(const StringName &p_signal, const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) {
+Error Object::connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) {
ERR_FAIL_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is null.");
Object *target_object = p_callable.get_object();
@@ -1350,7 +1221,6 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, co
conn.callable = target;
conn.signal = ::Signal(this, p_signal);
conn.flags = p_flags;
- conn.binds = p_binds;
slot.conn = conn;
slot.cE = target_object->connections.push_back(conn);
if (p_flags & CONNECT_REFERENCE_COUNTED) {
@@ -1598,7 +1468,7 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_signal_connection_list", "signal"), &Object::_get_signal_connection_list);
ClassDB::bind_method(D_METHOD("get_incoming_connections"), &Object::_get_incoming_connections);
- ClassDB::bind_method(D_METHOD("connect", "signal", "callable", "binds", "flags"), &Object::connect, DEFVAL(Array()), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("connect", "signal", "callable", "flags"), &Object::connect, DEFVAL(0));
ClassDB::bind_method(D_METHOD("disconnect", "signal", "callable"), &Object::disconnect);
ClassDB::bind_method(D_METHOD("is_connected", "signal", "callable"), &Object::is_connected);
diff --git a/core/object/object.h b/core/object/object.h
index 7cbedd29d9..35d0aaaa7d 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -47,13 +47,11 @@
enum PropertyHint {
PROPERTY_HINT_NONE, ///< no hint provided.
- PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_lesser][,noslider][,radians][,degrees][,exp][,suffix:<keyword>] range.
+ PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_lesser][,no_slider][,radians][,degrees][,exp][,suffix:<keyword>] range.
PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
PROPERTY_HINT_ENUM_SUGGESTION, ///< hint_text= "val1,val2,val3,etc"
- PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout")
- PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer)
+ PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "positive_only" to exclude in-out and out-in. (ie: "attenuation,positive_only")
PROPERTY_HINT_LINK,
- PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)
PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags)
PROPERTY_HINT_LAYERS_2D_RENDER,
PROPERTY_HINT_LAYERS_2D_PHYSICS,
@@ -67,6 +65,7 @@ enum PropertyHint {
PROPERTY_HINT_GLOBAL_DIR, ///< a directory path must be passed
PROPERTY_HINT_RESOURCE_TYPE, ///< a resource object type
PROPERTY_HINT_MULTILINE_TEXT, ///< used for string properties that can contain multiple lines
+ PROPERTY_HINT_EXPRESSION, ///< used for string properties that can contain multiple lines
PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties
PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color
PROPERTY_HINT_IMAGE_COMPRESS_LOSSY,
@@ -85,51 +84,52 @@ enum PropertyHint {
PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send
PROPERTY_HINT_NODE_PATH_VALID_TYPES,
PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog
+ PROPERTY_HINT_GLOBAL_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog
PROPERTY_HINT_INT_IS_OBJECTID,
PROPERTY_HINT_ARRAY_TYPE,
PROPERTY_HINT_INT_IS_POINTER,
PROPERTY_HINT_LOCALE_ID,
PROPERTY_HINT_LOCALIZABLE_STRING,
+ PROPERTY_HINT_NODE_TYPE, ///< a node object type
PROPERTY_HINT_MAX,
// When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit
};
enum PropertyUsageFlags {
PROPERTY_USAGE_NONE = 0,
- PROPERTY_USAGE_STORAGE = 1,
- PROPERTY_USAGE_EDITOR = 2,
- PROPERTY_USAGE_NETWORK = 4,
- PROPERTY_USAGE_EDITOR_HELPER = 8,
- PROPERTY_USAGE_CHECKABLE = 16, //used for editing global variables
- PROPERTY_USAGE_CHECKED = 32, //used for editing global variables
- PROPERTY_USAGE_INTERNATIONALIZED = 64, //hint for internationalized strings
- PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor
- PROPERTY_USAGE_CATEGORY = 256,
- PROPERTY_USAGE_SUBGROUP = 512,
- PROPERTY_USAGE_NO_INSTANCE_STATE = 2048,
- PROPERTY_USAGE_RESTART_IF_CHANGED = 4096,
- PROPERTY_USAGE_SCRIPT_VARIABLE = 8192,
- PROPERTY_USAGE_STORE_IF_NULL = 16384,
- PROPERTY_USAGE_ANIMATE_AS_TRIGGER = 32768,
- PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED = 65536,
- PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 17,
- PROPERTY_USAGE_CLASS_IS_ENUM = 1 << 18,
- PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 19,
- PROPERTY_USAGE_INTERNAL = 1 << 20,
- PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 21, // If the object is duplicated also this property will be duplicated
- PROPERTY_USAGE_HIGH_END_GFX = 1 << 22,
- PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 23,
- PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 24,
- PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 25, // Used in inspector to increment property when keyed in animation player
- PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 26, // when loading, the resource for this property can be set at the end of loading
- PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 27, // For Object properties, instantiate them when creating in editor.
- PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 28, //for project or editor settings, show when basic settings are selected
- PROPERTY_USAGE_READ_ONLY = 1 << 29, // Mark a property as read-only in the inspector.
- PROPERTY_USAGE_ARRAY = 1 << 30, // Used in the inspector to group properties as elements of an array.
-
- PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK,
- PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED,
- PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_NETWORK,
+ PROPERTY_USAGE_STORAGE = 1 << 1,
+ PROPERTY_USAGE_EDITOR = 1 << 2,
+ PROPERTY_USAGE_CHECKABLE = 1 << 3, // Used for editing global variables.
+ PROPERTY_USAGE_CHECKED = 1 << 4, // Used for editing global variables.
+ PROPERTY_USAGE_INTERNATIONALIZED = 1 << 5, // Hint for internationalized strings.
+ PROPERTY_USAGE_GROUP = 1 << 6, // Used for grouping props in the editor.
+ PROPERTY_USAGE_CATEGORY = 1 << 7,
+ PROPERTY_USAGE_SUBGROUP = 1 << 8,
+ PROPERTY_USAGE_CLASS_IS_BITFIELD = 1 << 9,
+ PROPERTY_USAGE_NO_INSTANCE_STATE = 1 << 10,
+ PROPERTY_USAGE_RESTART_IF_CHANGED = 1 << 11,
+ PROPERTY_USAGE_SCRIPT_VARIABLE = 1 << 12,
+ PROPERTY_USAGE_STORE_IF_NULL = 1 << 13,
+ PROPERTY_USAGE_ANIMATE_AS_TRIGGER = 1 << 14,
+ PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED = 1 << 15,
+ PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 16,
+ PROPERTY_USAGE_CLASS_IS_ENUM = 1 << 17,
+ PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 18,
+ PROPERTY_USAGE_INTERNAL = 1 << 19,
+ PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 20, // If the object is duplicated also this property will be duplicated.
+ PROPERTY_USAGE_HIGH_END_GFX = 1 << 21,
+ PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 22,
+ PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 23,
+ PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 24, // Used in inspector to increment property when keyed in animation player.
+ PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 25, // when loading, the resource for this property can be set at the end of loading.
+ PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 26, // For Object properties, instantiate them when creating in editor.
+ PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 27, //for project or editor settings, show when basic settings are selected.
+ PROPERTY_USAGE_READ_ONLY = 1 << 28, // Mark a property as read-only in the inspector.
+ PROPERTY_USAGE_ARRAY = 1 << 29, // Used in the inspector to group properties as elements of an array.
+
+ PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR,
+ PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNATIONALIZED,
+ PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE,
};
#define ADD_SIGNAL(m_signal) ::ClassDB::add_signal(get_class_static(), m_signal)
@@ -154,9 +154,7 @@ struct PropertyInfo {
String hint_string;
uint32_t usage = PROPERTY_USAGE_DEFAULT;
-#ifdef TOOLS_ENABLED
- Vector<String> linked_properties;
-#endif
+ // If you are thinking about adding another member to this class, ask the maintainer (Juan) first.
_FORCE_INLINE_ PropertyInfo added_usage(uint32_t p_fl) const {
PropertyInfo pi = *this;
@@ -187,6 +185,14 @@ struct PropertyInfo {
type(Variant::OBJECT),
class_name(p_class_name) {}
+ explicit PropertyInfo(const GDNativePropertyInfo &pinfo) :
+ type((Variant::Type)pinfo.type),
+ name(pinfo.name),
+ class_name(pinfo.class_name), // can be null
+ hint((PropertyHint)pinfo.hint),
+ hint_string(pinfo.hint_string), // can be null
+ usage(pinfo.usage) {}
+
bool operator==(const PropertyInfo &p_info) const {
return ((type == p_info.type) &&
(name == p_info.name) &&
@@ -203,10 +209,21 @@ struct PropertyInfo {
Array convert_property_list(const List<PropertyInfo> *p_list);
+enum MethodFlags {
+ METHOD_FLAG_NORMAL = 1,
+ METHOD_FLAG_EDITOR = 2,
+ METHOD_FLAG_CONST = 4,
+ METHOD_FLAG_VIRTUAL = 8,
+ METHOD_FLAG_VARARG = 16,
+ METHOD_FLAG_STATIC = 32,
+ METHOD_FLAG_OBJECT_CORE = 64,
+ METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL,
+};
+
struct MethodInfo {
String name;
PropertyInfo return_val;
- uint32_t flags; // NOLINT - prevent clang-tidy to assign method_bind.h constant here, it should stay in .cpp.
+ uint32_t flags = METHOD_FLAGS_DEFAULT;
int id = 0;
List<PropertyInfo> arguments;
Vector<Variant> default_arguments;
@@ -218,26 +235,50 @@ struct MethodInfo {
static MethodInfo from_dict(const Dictionary &p_dict);
- MethodInfo();
- MethodInfo(const String &p_name);
- MethodInfo(const String &p_name, const PropertyInfo &p_param1);
- MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2);
- MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3);
- MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4);
- MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5);
- MethodInfo(Variant::Type ret);
- MethodInfo(Variant::Type ret, const String &p_name);
- MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1);
- MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2);
- MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3);
- MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4);
- MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5);
- MethodInfo(const PropertyInfo &p_ret, const String &p_name);
- MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1);
- MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2);
- MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3);
- MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4);
- MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5);
+ MethodInfo() {}
+
+ void _push_params(const PropertyInfo &p_param) {
+ arguments.push_back(p_param);
+ }
+
+ template <typename... VarArgs>
+ void _push_params(const PropertyInfo &p_param, VarArgs... p_params) {
+ arguments.push_back(p_param);
+ _push_params(p_params...);
+ }
+
+ MethodInfo(const String &p_name) { name = p_name; }
+
+ template <typename... VarArgs>
+ MethodInfo(const String &p_name, VarArgs... p_params) {
+ name = p_name;
+ _push_params(p_params...);
+ }
+
+ MethodInfo(Variant::Type ret) { return_val.type = ret; }
+ MethodInfo(Variant::Type ret, const String &p_name) {
+ return_val.type = ret;
+ name = p_name;
+ }
+
+ template <typename... VarArgs>
+ MethodInfo(Variant::Type ret, const String &p_name, VarArgs... p_params) {
+ name = p_name;
+ return_val.type = ret;
+ _push_params(p_params...);
+ }
+
+ MethodInfo(const PropertyInfo &p_ret, const String &p_name) {
+ return_val = p_ret;
+ name = p_name;
+ }
+
+ template <typename... VarArgs>
+ MethodInfo(const PropertyInfo &p_ret, const String &p_name, VarArgs... p_params) {
+ return_val = p_ret;
+ name = p_name;
+ _push_params(p_params...);
+ }
};
// API used to extend in GDNative and other C compatible compiled languages.
@@ -315,6 +356,7 @@ private:
friend class ::ClassDB; \
\
public: \
+ static constexpr bool _class_is_enabled = !bool(GD_IS_DEFINED(ClassDB_Disable_##m_class)) && m_inherits::_class_is_enabled; \
virtual String get_class() const override { \
if (_get_extension()) { \
return _get_extension()->class_name.operator String(); \
@@ -466,7 +508,6 @@ public:
Callable callable;
uint32_t flags = 0;
- Vector<Variant> binds;
bool operator<(const Connection &p_conn) const;
operator Variant() const;
@@ -624,6 +665,8 @@ public: // Should be protected, but bug in clang++.
_FORCE_INLINE_ static void register_custom_data_to_otdb() {}
public:
+ static constexpr bool _class_is_enabled = true;
+
void notify_property_list_changed();
static void *get_class_ptr_static() {
@@ -719,6 +762,7 @@ public:
void get_method_list(List<MethodInfo> *p_list) const;
Variant callv(const StringName &p_method, const Array &p_args);
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
template <typename... VarArgs>
Variant call(const StringName &p_method, VarArgs... p_args) {
@@ -782,7 +826,7 @@ public:
int get_persistent_signal_connection_count() const;
void get_signals_connected_to_this(List<Connection> *p_connections) const;
- Error connect(const StringName &p_signal, const Callable &p_callable, const Vector<Variant> &p_binds = Vector<Variant>(), uint32_t p_flags = 0);
+ Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0);
void disconnect(const StringName &p_signal, const Callable &p_callable);
bool is_connected(const StringName &p_signal, const Callable &p_callable) const;
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index 66c9a80193..b06c2e8896 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -101,6 +101,31 @@ Dictionary Script::_get_script_constant_map() {
return ret;
}
+#ifdef TOOLS_ENABLED
+
+PropertyInfo Script::get_class_category() const {
+ String path = get_path();
+ String name;
+
+ if (is_built_in()) {
+ if (get_name().is_empty()) {
+ name = TTR("Built-in script");
+ } else {
+ name = vformat("%s (%s)", get_name(), TTR("Built-in"));
+ }
+ } else {
+ if (get_name().is_empty()) {
+ name = path.get_file();
+ } else {
+ name = get_name();
+ }
+ }
+
+ return PropertyInfo(Variant::NIL, name, PROPERTY_HINT_NONE, path, PROPERTY_USAGE_CATEGORY);
+}
+
+#endif // TOOLS_ENABLED
+
void Script::_bind_methods() {
ClassDB::bind_method(D_METHOD("can_instantiate"), &Script::can_instantiate);
//ClassDB::bind_method(D_METHOD("instance_create","base_object"),&Script::instance_create);
@@ -295,6 +320,11 @@ void ScriptServer::save_global_classes() {
}
////////////////////
+
+Variant ScriptInstance::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ return callp(p_method, p_args, p_argcount, r_error);
+}
+
void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) {
List<PropertyInfo> pinfo;
get_property_list(&pinfo);
@@ -339,11 +369,14 @@ void ScriptLanguage::get_core_type_words(List<String> *p_core_type_words) const
p_core_type_words->push_back("Vector3");
p_core_type_words->push_back("Vector3i");
p_core_type_words->push_back("Transform2D");
+ p_core_type_words->push_back("Vector4");
+ p_core_type_words->push_back("Vector4i");
p_core_type_words->push_back("Plane");
p_core_type_words->push_back("Quaternion");
p_core_type_words->push_back("AABB");
p_core_type_words->push_back("Basis");
p_core_type_words->push_back("Transform3D");
+ p_core_type_words->push_back("Projection");
p_core_type_words->push_back("Color");
p_core_type_words->push_back("StringName");
p_core_type_words->push_back("NodePath");
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 0a8e79a24e..f5f052b600 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -33,7 +33,6 @@
#include "core/doc_data.h"
#include "core/io/resource.h"
-#include "core/multiplayer/multiplayer.h"
#include "core/templates/pair.h"
#include "core/templates/rb_map.h"
@@ -133,6 +132,7 @@ public:
#ifdef TOOLS_ENABLED
virtual Vector<DocData::ClassDoc> get_documentation() const = 0;
+ virtual PropertyInfo get_class_category() const;
#endif // TOOLS_ENABLED
virtual bool has_method(const StringName &p_method) const = 0;
@@ -159,7 +159,7 @@ public:
virtual bool is_placeholder_fallback_enabled() const { return false; }
- virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0;
+ virtual const Variant get_rpc_config() const = 0;
Script() {}
};
@@ -190,6 +190,7 @@ public:
return callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr);
}
+ virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); // implement if language supports const functions
virtual void notification(int p_notification) = 0;
virtual String to_string(bool *r_valid) {
if (r_valid) {
@@ -212,7 +213,7 @@ public:
virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid);
virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid);
- virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const { return get_script()->get_rpc_methods(); }
+ virtual const Variant get_rpc_config() const { return get_script()->get_rpc_config(); }
virtual ScriptLanguage *get_language() = 0;
virtual ~ScriptInstance();
@@ -349,6 +350,7 @@ public:
LOOKUP_RESULT_CLASS_SIGNAL,
LOOKUP_RESULT_CLASS_ENUM,
LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE,
+ LOOKUP_RESULT_CLASS_ANNOTATION,
LOOKUP_RESULT_MAX
};
@@ -401,6 +403,7 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const = 0;
virtual void get_public_functions(List<MethodInfo> *p_functions) const = 0;
virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const = 0;
+ virtual void get_public_annotations(List<MethodInfo> *p_annotations) const = 0;
struct ProfilingInfo {
StringName signature;
@@ -466,7 +469,7 @@ public:
virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr) override;
virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = nullptr) override;
- virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override { return Vector<Multiplayer::RPCConfig>(); }
+ virtual const Variant get_rpc_config() const override { return Variant(); }
PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner);
~PlaceHolderScriptInstance();
diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp
index 5af79bbea3..9de784ea7f 100644
--- a/core/object/script_language_extension.cpp
+++ b/core/object/script_language_extension.cpp
@@ -74,7 +74,7 @@ void ScriptExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_members);
GDVIRTUAL_BIND(_is_placeholder_fallback_enabled);
- GDVIRTUAL_BIND(_get_rpc_methods);
+ GDVIRTUAL_BIND(_get_rpc_config);
}
void ScriptLanguageExtension::_bind_methods() {
@@ -134,6 +134,7 @@ void ScriptLanguageExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_recognized_extensions);
GDVIRTUAL_BIND(_get_public_functions);
GDVIRTUAL_BIND(_get_public_constants);
+ GDVIRTUAL_BIND(_get_public_annotations);
GDVIRTUAL_BIND(_profiling_start);
GDVIRTUAL_BIND(_profiling_stop);
@@ -160,6 +161,7 @@ void ScriptLanguageExtension::_bind_methods() {
BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_SIGNAL);
BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_ENUM);
BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE);
+ BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_ANNOTATION);
BIND_ENUM_CONSTANT(LOOKUP_RESULT_MAX);
BIND_ENUM_CONSTANT(LOCATION_LOCAL);
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index 406a431a11..2869f4ad98 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -173,28 +173,12 @@ public:
EXBIND0RC(bool, is_placeholder_fallback_enabled)
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_rpc_methods)
+ GDVIRTUAL0RC(Variant, _get_rpc_config)
- virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override {
- TypedArray<Dictionary> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_rpc_methods, ret);
- Vector<Multiplayer::RPCConfig> rpcret;
- for (int i = 0; i < ret.size(); i++) {
- Dictionary d = ret[i];
- Multiplayer::RPCConfig rpc;
- ERR_CONTINUE(!d.has("name"));
- rpc.name = d["name"];
- ERR_CONTINUE(!d.has("rpc_mode"));
- rpc.rpc_mode = Multiplayer::RPCMode(int(d["rpc_mode"]));
- ERR_CONTINUE(!d.has("call_local"));
- rpc.call_local = d["call_local"];
- ERR_CONTINUE(!d.has("transfer_mode"));
- rpc.transfer_mode = Multiplayer::TransferMode(int(d["transfer_mode"]));
- ERR_CONTINUE(!d.has("channel"));
- rpc.channel = d["channel"];
- rpcret.push_back(rpc);
- }
- return rpcret;
+ virtual const Variant get_rpc_config() const override {
+ Variant ret;
+ GDVIRTUAL_REQUIRED_CALL(_get_rpc_config, ret);
+ return ret;
}
ScriptExtension() {}
@@ -580,6 +564,15 @@ public:
p_constants->push_back(Pair<String, Variant>(d["name"], d["value"]));
}
}
+ GDVIRTUAL0RC(TypedArray<Dictionary>, _get_public_annotations)
+ virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override {
+ TypedArray<Dictionary> ret;
+ GDVIRTUAL_REQUIRED_CALL(_get_public_annotations, ret);
+ for (int i = 0; i < ret.size(); i++) {
+ MethodInfo mi = MethodInfo::from_dict(ret[i]);
+ p_annotations->push_back(mi);
+ }
+ }
EXBIND0(profiling_start)
EXBIND0(profiling_stop)
@@ -670,8 +663,16 @@ public:
if (native_info->get_property_list_func) {
uint32_t pcount;
const GDNativePropertyInfo *pinfo = native_info->get_property_list_func(instance, &pcount);
+
+#ifdef TOOLS_ENABLED
+ Ref<Script> script = get_script();
+ if (script->is_valid() && pcount > 0) {
+ p_list->push_back(script->get_class_category());
+ }
+#endif // TOOLS_ENABLED
+
for (uint32_t i = 0; i < pcount; i++) {
- p_list->push_back(PropertyInfo(Variant::Type(pinfo[i].type), pinfo[i].class_name, PropertyHint(pinfo[i].hint), pinfo[i].hint_string, pinfo[i].usage, pinfo[i].class_name));
+ p_list->push_back(PropertyInfo(pinfo[i]));
}
if (native_info->free_property_list_func) {
native_info->free_property_list_func(instance, pinfo);
@@ -716,9 +717,9 @@ public:
m.name = minfo[i].name;
m.flags = minfo[i].flags;
m.id = minfo[i].id;
- m.return_val = PropertyInfo(Variant::Type(minfo[i].return_value.type), minfo[i].return_value.class_name, PropertyHint(minfo[i].return_value.hint), minfo[i].return_value.hint_string, minfo[i].return_value.usage, minfo[i].return_value.class_name);
+ m.return_val = PropertyInfo(minfo[i].return_value);
for (uint32_t j = 0; j < minfo[i].argument_count; j++) {
- m.arguments.push_back(PropertyInfo(Variant::Type(minfo[i].arguments[j].type), minfo[i].arguments[j].class_name, PropertyHint(minfo[i].arguments[j].hint), minfo[i].arguments[j].hint_string, minfo[i].arguments[j].usage, minfo[i].arguments[j].class_name));
+ m.arguments.push_back(PropertyInfo(minfo[i].arguments[j]));
}
const Variant *def_values = (const Variant *)minfo[i].default_arguments;
for (uint32_t j = 0; j < minfo[i].default_argument_count; j++) {
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
new file mode 100644
index 0000000000..c770515b9e
--- /dev/null
+++ b/core/object/worker_thread_pool.cpp
@@ -0,0 +1,474 @@
+/*************************************************************************/
+/* worker_thread_pool.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "worker_thread_pool.h"
+
+#include "core/os/os.h"
+
+void WorkerThreadPool::Task::free_template_userdata() {
+ ERR_FAIL_COND(!template_userdata);
+ ERR_FAIL_COND(native_func_userdata == nullptr);
+ BaseTemplateUserdata *btu = (BaseTemplateUserdata *)native_func_userdata;
+ memdelete(btu);
+}
+
+WorkerThreadPool *WorkerThreadPool::singleton = nullptr;
+
+void WorkerThreadPool::_process_task_queue() {
+ task_mutex.lock();
+ Task *task = task_queue.first()->self();
+ task_queue.remove(task_queue.first());
+ task_mutex.unlock();
+ _process_task(task);
+}
+
+void WorkerThreadPool::_process_task(Task *p_task) {
+ bool low_priority = p_task->low_priority;
+
+ if (p_task->group) {
+ // Handling a group
+ bool do_post = false;
+ Callable::CallError ce;
+ Variant ret;
+ Variant arg;
+ Variant *argptr = &arg;
+
+ while (true) {
+ uint32_t work_index = p_task->group->index.postincrement();
+
+ if (work_index >= p_task->group->max) {
+ break;
+ }
+ if (p_task->native_group_func) {
+ p_task->native_group_func(p_task->native_func_userdata, work_index);
+ } else if (p_task->template_userdata) {
+ p_task->template_userdata->callback_indexed(work_index);
+ } else {
+ arg = work_index;
+ p_task->callable.callp((const Variant **)&argptr, 1, ret, ce);
+ }
+
+ // This is the only way to ensure posting is done when all tasks are really complete.
+ uint32_t completed_amount = p_task->group->completed_index.increment();
+
+ if (completed_amount == p_task->group->max) {
+ do_post = true;
+ }
+ }
+
+ if (do_post && p_task->template_userdata) {
+ memdelete(p_task->template_userdata); // This is no longer needed at this point, so get rid of it.
+ }
+
+ if (low_priority && use_native_low_priority_threads) {
+ p_task->completed = true;
+ p_task->done_semaphore.post();
+ if (do_post) {
+ p_task->group->completed.set_to(true);
+ }
+ } else {
+ if (do_post) {
+ p_task->group->done_semaphore.post();
+ p_task->group->completed.set_to(true);
+ }
+ uint32_t max_users = p_task->group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment.
+ uint32_t finished_users = p_task->group->finished.increment();
+
+ if (finished_users == max_users) {
+ // Get rid of the group, because nobody else is using it.
+ task_mutex.lock();
+ group_allocator.free(p_task->group);
+ task_mutex.unlock();
+ }
+
+ // For groups, tasks get rid of themselves.
+
+ task_mutex.lock();
+ task_allocator.free(p_task);
+ task_mutex.unlock();
+ }
+ } else {
+ if (p_task->native_func) {
+ p_task->native_func(p_task->native_func_userdata);
+ } else if (p_task->template_userdata) {
+ p_task->template_userdata->callback();
+ memdelete(p_task->template_userdata);
+ } else {
+ Callable::CallError ce;
+ Variant ret;
+ p_task->callable.callp(nullptr, 0, ret, ce);
+ }
+
+ p_task->completed = true;
+ p_task->done_semaphore.post();
+ }
+
+ if (!use_native_low_priority_threads && low_priority) {
+ // A low prioriry task was freed, so see if we can move a pending one to the high priority queue.
+ bool post = false;
+ task_mutex.lock();
+ if (low_priority_task_queue.first()) {
+ Task *low_prio_task = low_priority_task_queue.first()->self();
+ low_priority_task_queue.remove(low_priority_task_queue.first());
+ task_queue.add_last(&low_prio_task->task_elem);
+ post = true;
+ } else {
+ low_priority_threads_used.decrement();
+ }
+ task_mutex.lock();
+ if (post) {
+ task_available_semaphore.post();
+ }
+ }
+}
+
+void WorkerThreadPool::_thread_function(void *p_user) {
+ while (true) {
+ singleton->task_available_semaphore.wait();
+ if (singleton->exit_threads.is_set()) {
+ break;
+ }
+ singleton->_process_task_queue();
+ }
+}
+
+void WorkerThreadPool::_native_low_priority_thread_function(void *p_user) {
+ Task *task = (Task *)p_user;
+ singleton->_process_task(task);
+}
+
+void WorkerThreadPool::_post_task(Task *p_task, bool p_high_priority) {
+ task_mutex.lock();
+ p_task->low_priority = !p_high_priority;
+ if (!p_high_priority && use_native_low_priority_threads) {
+ task_mutex.unlock();
+ p_task->low_priority_thread = native_thread_allocator.alloc();
+ p_task->low_priority_thread->start(_native_low_priority_thread_function, p_task); // Pask task directly to thread.
+
+ } else if (p_high_priority || low_priority_threads_used.get() < max_low_priority_threads) {
+ task_queue.add_last(&p_task->task_elem);
+ if (!p_high_priority) {
+ low_priority_threads_used.increment();
+ }
+ task_mutex.unlock();
+ task_available_semaphore.post();
+ } else {
+ // Too many threads using low priority, must go to queue.
+ low_priority_task_queue.add_last(&p_task->task_elem);
+ task_mutex.unlock();
+ }
+}
+
+WorkerThreadPool::TaskID WorkerThreadPool::add_native_task(void (*p_func)(void *), void *p_userdata, bool p_high_priority, const String &p_description) {
+ return _add_task(Callable(), p_func, p_userdata, nullptr, p_high_priority, p_description);
+}
+
+WorkerThreadPool::TaskID WorkerThreadPool::_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) {
+ task_mutex.lock();
+ // Get a free task
+ Task *task = task_allocator.alloc();
+ TaskID id = last_task++;
+ task->callable = p_callable;
+ task->native_func = p_func;
+ task->native_func_userdata = p_userdata;
+ task->description = p_description;
+ task->template_userdata = p_template_userdata;
+ tasks.insert(id, task);
+ task_mutex.unlock();
+
+ _post_task(task, p_high_priority);
+
+ return id;
+}
+
+WorkerThreadPool::TaskID WorkerThreadPool::add_task(const Callable &p_action, bool p_high_priority, const String &p_description) {
+ return _add_task(p_action, nullptr, nullptr, nullptr, p_high_priority, p_description);
+}
+
+bool WorkerThreadPool::is_task_completed(TaskID p_task_id) const {
+ task_mutex.lock();
+ const Task *const *taskp = tasks.getptr(p_task_id);
+ if (!taskp) {
+ task_mutex.unlock();
+ ERR_FAIL_V_MSG(false, "Invalid Task ID"); // Invalid task
+ }
+
+ bool completed = (*taskp)->completed;
+ task_mutex.unlock();
+
+ return completed;
+}
+
+void WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
+ task_mutex.lock();
+ Task **taskp = tasks.getptr(p_task_id);
+ if (!taskp) {
+ task_mutex.unlock();
+ ERR_FAIL_MSG("Invalid Task ID"); // Invalid task
+ }
+ Task *task = *taskp;
+
+ if (task->waiting) {
+ String description = task->description;
+ task_mutex.unlock();
+ if (description.is_empty()) {
+ ERR_FAIL_MSG("Another thread is waiting on this task: " + itos(p_task_id)); // Invalid task
+ } else {
+ ERR_FAIL_MSG("Another thread is waiting on this task: " + description + " (" + itos(p_task_id) + ")"); // Invalid task
+ }
+ }
+
+ task->waiting = true;
+
+ task_mutex.unlock();
+
+ if (use_native_low_priority_threads && task->low_priority) {
+ task->low_priority_thread->wait_to_finish();
+ native_thread_allocator.free(task->low_priority_thread);
+ } else {
+ int *index = thread_ids.getptr(Thread::get_caller_id());
+
+ if (index) {
+ // We are an actual process thread, we must not be blocked so continue processing stuff if available.
+ while (true) {
+ if (task->done_semaphore.try_wait()) {
+ // If done, exit
+ break;
+ }
+ if (task_available_semaphore.try_wait()) {
+ // Solve tasks while they are around.
+ _process_task_queue();
+ continue;
+ }
+ OS::get_singleton()->delay_usec(1); // Microsleep, this could be converted to waiting for multiple objects in supported platforms for a bit more performance.
+ }
+ } else {
+ task->done_semaphore.wait();
+ }
+ }
+
+ task_mutex.lock();
+ tasks.erase(p_task_id);
+ task_allocator.free(task);
+ task_mutex.unlock();
+}
+
+WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) {
+ ERR_FAIL_COND_V(p_elements < 0, INVALID_TASK_ID);
+ if (p_tasks < 0) {
+ p_tasks = threads.size();
+ }
+
+ task_mutex.lock();
+ Group *group = group_allocator.alloc();
+ GroupID id = last_task++;
+ group->max = p_elements;
+ group->self = id;
+
+ Task **tasks_posted = nullptr;
+ if (p_elements == 0) {
+ // Should really not call it with zero Elements, but at least it should work.
+ group->completed.set_to(true);
+ group->done_semaphore.post();
+ group->tasks_used = 0;
+ p_tasks = 0;
+ if (p_template_userdata) {
+ memdelete(p_template_userdata);
+ }
+
+ } else {
+ group->tasks_used = p_tasks;
+ tasks_posted = (Task **)alloca(sizeof(Task *) * p_tasks);
+ for (int i = 0; i < p_tasks; i++) {
+ Task *task = task_allocator.alloc();
+ task->native_group_func = p_func;
+ task->native_func_userdata = p_userdata;
+ task->description = p_description;
+ task->group = group;
+ task->callable = p_callable;
+ task->template_userdata = p_template_userdata;
+ tasks_posted[i] = task;
+ // No task ID is used.
+ }
+ }
+
+ groups[id] = group;
+ task_mutex.unlock();
+
+ if (!p_high_priority && use_native_low_priority_threads) {
+ group->low_priority_native_tasks.resize(p_tasks);
+ }
+
+ for (int i = 0; i < p_tasks; i++) {
+ _post_task(tasks_posted[i], p_high_priority);
+ if (!p_high_priority && use_native_low_priority_threads) {
+ group->low_priority_native_tasks[i] = tasks_posted[i];
+ }
+ }
+
+ return id;
+}
+
+WorkerThreadPool::GroupID WorkerThreadPool::add_native_group_task(void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) {
+ return _add_group_task(Callable(), p_func, p_userdata, nullptr, p_elements, p_tasks, p_high_priority, p_description);
+}
+
+WorkerThreadPool::GroupID WorkerThreadPool::add_group_task(const Callable &p_action, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) {
+ return _add_group_task(p_action, nullptr, nullptr, nullptr, p_elements, p_tasks, p_high_priority, p_description);
+}
+
+uint32_t WorkerThreadPool::get_group_processed_element_count(GroupID p_group) const {
+ task_mutex.lock();
+ const Group *const *groupp = groups.getptr(p_group);
+ if (!groupp) {
+ task_mutex.unlock();
+ ERR_FAIL_V_MSG(0, "Invalid Group ID");
+ }
+ uint32_t elements = (*groupp)->completed_index.get();
+ task_mutex.unlock();
+ return elements;
+}
+bool WorkerThreadPool::is_group_task_completed(GroupID p_group) const {
+ task_mutex.lock();
+ const Group *const *groupp = groups.getptr(p_group);
+ if (!groupp) {
+ task_mutex.unlock();
+ ERR_FAIL_V_MSG(false, "Invalid Group ID");
+ }
+ bool completed = (*groupp)->completed.is_set();
+ task_mutex.unlock();
+ return completed;
+}
+
+void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) {
+ task_mutex.lock();
+ Group **groupp = groups.getptr(p_group);
+ task_mutex.unlock();
+ if (!groupp) {
+ ERR_FAIL_MSG("Invalid Group ID");
+ }
+ Group *group = *groupp;
+
+ if (group->low_priority_native_tasks.size() > 0) {
+ for (uint32_t i = 0; i < group->low_priority_native_tasks.size(); i++) {
+ group->low_priority_native_tasks[i]->low_priority_thread->wait_to_finish();
+ native_thread_allocator.free(group->low_priority_native_tasks[i]->low_priority_thread);
+ task_mutex.lock();
+ task_allocator.free(group->low_priority_native_tasks[i]);
+ task_mutex.unlock();
+ }
+
+ task_mutex.lock();
+ group_allocator.free(group);
+ task_mutex.unlock();
+ } else {
+ group->done_semaphore.wait();
+
+ uint32_t max_users = group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment.
+ uint32_t finished_users = group->finished.increment(); // fetch happens before inc, so increment later.
+
+ if (finished_users == max_users) {
+ // All tasks using this group are gone (finished before the group), so clear the gorup too.
+ task_mutex.lock();
+ group_allocator.free(group);
+ task_mutex.unlock();
+ }
+ }
+
+ groups.erase(p_group); // Threads do not access this, so safe to erase here.
+}
+
+void WorkerThreadPool::init(int p_thread_count, bool p_use_native_threads_low_priority, float p_low_priority_task_ratio) {
+ ERR_FAIL_COND(threads.size() > 0);
+ if (p_thread_count < 0) {
+ p_thread_count = OS::get_singleton()->get_default_thread_pool_size();
+ }
+
+ if (p_use_native_threads_low_priority) {
+ max_low_priority_threads = 0;
+ } else {
+ max_low_priority_threads = CLAMP(p_thread_count * p_low_priority_task_ratio, 1, p_thread_count);
+ }
+
+ use_native_low_priority_threads = p_use_native_threads_low_priority;
+
+ threads.resize(p_thread_count);
+
+ for (uint32_t i = 0; i < threads.size(); i++) {
+ threads[i].index = i;
+ threads[i].thread.start(&WorkerThreadPool::_thread_function, &threads[i]);
+ thread_ids.insert(threads[i].thread.get_id(), i);
+ }
+}
+
+void WorkerThreadPool::finish() {
+ if (threads.size() == 0) {
+ return;
+ }
+
+ task_mutex.lock();
+ SelfList<Task> *E = low_priority_task_queue.first();
+ while (E) {
+ print_error("Task waiting was never re-claimed: " + E->self()->description);
+ E = E->next();
+ }
+ task_mutex.unlock();
+
+ exit_threads.set_to(true);
+
+ for (uint32_t i = 0; i < threads.size(); i++) {
+ task_available_semaphore.post();
+ }
+
+ for (uint32_t i = 0; i < threads.size(); i++) {
+ threads[i].thread.wait_to_finish();
+ }
+
+ threads.clear();
+}
+
+void WorkerThreadPool::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_task", "action", "high_priority", "description"), &WorkerThreadPool::add_task, DEFVAL(false), DEFVAL(String()));
+ ClassDB::bind_method(D_METHOD("is_task_completed", "task_id"), &WorkerThreadPool::is_task_completed);
+ ClassDB::bind_method(D_METHOD("wait_for_task_completion", "task_id"), &WorkerThreadPool::wait_for_task_completion);
+
+ ClassDB::bind_method(D_METHOD("add_group_task", "action", "elements", "tasks_needed", "high_priority", "description"), &WorkerThreadPool::add_group_task, DEFVAL(-1), DEFVAL(false), DEFVAL(String()));
+ ClassDB::bind_method(D_METHOD("is_group_task_completed", "group_id"), &WorkerThreadPool::is_group_task_completed);
+ ClassDB::bind_method(D_METHOD("get_group_processed_element_count", "group_id"), &WorkerThreadPool::get_group_processed_element_count);
+ ClassDB::bind_method(D_METHOD("wait_for_group_task_completion", "group_id"), &WorkerThreadPool::wait_for_group_task_completion);
+}
+
+WorkerThreadPool::WorkerThreadPool() {
+ singleton = this;
+}
+
+WorkerThreadPool::~WorkerThreadPool() {
+ finish();
+}
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
new file mode 100644
index 0000000000..1debd9ca37
--- /dev/null
+++ b/core/object/worker_thread_pool.h
@@ -0,0 +1,198 @@
+/*************************************************************************/
+/* worker_thread_pool.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 WORKER_THREAD_POOL_H
+#define WORKER_THREAD_POOL_H
+
+#include "core/os/memory.h"
+#include "core/os/os.h"
+#include "core/os/semaphore.h"
+#include "core/os/thread.h"
+#include "core/templates/local_vector.h"
+#include "core/templates/paged_allocator.h"
+#include "core/templates/rid.h"
+#include "core/templates/safe_refcount.h"
+
+class WorkerThreadPool : public Object {
+ GDCLASS(WorkerThreadPool, Object)
+public:
+ enum {
+ INVALID_TASK_ID = -1
+ };
+
+ typedef int64_t TaskID;
+ typedef int64_t GroupID;
+
+private:
+ struct Task;
+
+ struct BaseTemplateUserdata {
+ virtual void callback() {}
+ virtual void callback_indexed(uint32_t p_index) {}
+ virtual ~BaseTemplateUserdata() {}
+ };
+
+ struct Group {
+ GroupID self;
+ SafeNumeric<uint32_t> index;
+ SafeNumeric<uint32_t> completed_index;
+ uint32_t max = 0;
+ Semaphore done_semaphore;
+ SafeFlag completed;
+ SafeNumeric<uint32_t> finished;
+ uint32_t tasks_used = 0;
+ TightLocalVector<Task *> low_priority_native_tasks;
+ };
+
+ struct Task {
+ Callable callable;
+ void (*native_func)(void *) = nullptr;
+ void (*native_group_func)(void *, uint32_t) = nullptr;
+ void *native_func_userdata = nullptr;
+ String description;
+ Semaphore done_semaphore;
+ bool completed = false;
+ Group *group = nullptr;
+ SelfList<Task> task_elem;
+ bool waiting = false; // Waiting for completion
+ bool low_priority = false;
+ BaseTemplateUserdata *template_userdata = nullptr;
+ Thread *low_priority_thread = nullptr;
+
+ void free_template_userdata();
+ Task() :
+ task_elem(this) {}
+ };
+
+ PagedAllocator<Task> task_allocator;
+ PagedAllocator<Group> group_allocator;
+ PagedAllocator<Thread> native_thread_allocator;
+
+ SelfList<Task>::List low_priority_task_queue;
+ SelfList<Task>::List task_queue;
+
+ Mutex task_mutex;
+ Semaphore task_available_semaphore;
+
+ struct ThreadData {
+ uint32_t index;
+ Thread thread;
+ };
+
+ TightLocalVector<ThreadData> threads;
+ SafeFlag exit_threads;
+
+ HashMap<Thread::ID, int> thread_ids;
+ HashMap<TaskID, Task *> tasks;
+ HashMap<GroupID, Group *> groups;
+
+ bool use_native_low_priority_threads = false;
+ uint32_t max_low_priority_threads = 0;
+ SafeNumeric<uint32_t> low_priority_threads_used;
+
+ uint64_t last_task = 1;
+
+ static void _thread_function(void *p_user);
+ static void _native_low_priority_thread_function(void *p_user);
+
+ void _process_task_queue();
+ void _process_task(Task *task);
+
+ void _post_task(Task *p_task, bool p_high_priority);
+
+ static WorkerThreadPool *singleton;
+
+ 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);
+ GroupID _add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description);
+
+ template <class C, class M, class U>
+ struct TaskUserData : public BaseTemplateUserdata {
+ C *instance;
+ M method;
+ U userdata;
+ virtual void callback() override {
+ (instance->*method)(userdata);
+ }
+ };
+
+ template <class C, class M, class U>
+ struct GroupUserData : public BaseTemplateUserdata {
+ C *instance;
+ M method;
+ U userdata;
+ virtual void callback_indexed(uint32_t p_index) override {
+ (instance->*method)(p_index, userdata);
+ }
+ };
+
+protected:
+ static void _bind_methods();
+
+public:
+ template <class C, class M, class U>
+ TaskID add_template_task(C *p_instance, M p_method, U p_userdata, bool p_high_priority = false, const String &p_description = String()) {
+ typedef TaskUserData<C, M, U> TUD;
+ TUD *ud = memnew(TUD);
+ ud->instance = p_instance;
+ ud->method = p_method;
+ ud->userdata = p_userdata;
+ return _add_task(Callable(), nullptr, nullptr, ud, p_high_priority, p_description);
+ }
+ TaskID add_native_task(void (*p_func)(void *), void *p_userdata, bool p_high_priority = false, const String &p_description = String());
+ TaskID add_task(const Callable &p_action, bool p_high_priority = false, const String &p_description = String());
+
+ bool is_task_completed(TaskID p_task_id) const;
+ void wait_for_task_completion(TaskID p_task_id);
+
+ template <class C, class M, class U>
+ GroupID add_template_group_task(C *p_instance, M p_method, U p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()) {
+ typedef GroupUserData<C, M, U> GUD;
+ GUD *ud = memnew(GUD);
+ ud->instance = p_instance;
+ ud->method = p_method;
+ ud->userdata = p_userdata;
+ return _add_group_task(Callable(), nullptr, nullptr, ud, p_elements, p_tasks, p_high_priority, p_description);
+ }
+ GroupID add_native_group_task(void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String());
+ GroupID add_group_task(const Callable &p_action, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String());
+ uint32_t get_group_processed_element_count(GroupID p_group) const;
+ bool is_group_task_completed(GroupID p_group) const;
+ void wait_for_group_task_completion(GroupID p_group);
+
+ _FORCE_INLINE_ int get_thread_count() const { return threads.size(); }
+
+ static WorkerThreadPool *get_singleton() { return singleton; }
+ void init(int p_thread_count = -1, bool p_use_native_threads_low_priority = true, float p_low_priority_task_ratio = 0.3);
+ void finish();
+ WorkerThreadPool();
+ ~WorkerThreadPool();
+};
+
+#endif // WORKER_THREAD_POOL_H