diff options
Diffstat (limited to 'core/object')
-rw-r--r-- | core/object/callable_method_pointer.cpp | 4 | ||||
-rw-r--r-- | core/object/callable_method_pointer.h | 82 | ||||
-rw-r--r-- | core/object/class_db.cpp | 408 | ||||
-rw-r--r-- | core/object/class_db.h | 77 | ||||
-rw-r--r-- | core/object/message_queue.cpp | 8 | ||||
-rw-r--r-- | core/object/method_bind.cpp | 25 | ||||
-rw-r--r-- | core/object/method_bind.h | 14 | ||||
-rw-r--r-- | core/object/object.cpp | 341 | ||||
-rw-r--r-- | core/object/object.h | 186 | ||||
-rw-r--r-- | core/object/ref_counted.cpp | 4 | ||||
-rw-r--r-- | core/object/ref_counted.h | 4 | ||||
-rw-r--r-- | core/object/script_language.cpp | 41 | ||||
-rw-r--r-- | core/object/script_language.h | 27 | ||||
-rw-r--r-- | core/object/script_language_extension.cpp | 5 | ||||
-rw-r--r-- | core/object/script_language_extension.h | 47 | ||||
-rw-r--r-- | core/object/worker_thread_pool.cpp | 474 | ||||
-rw-r--r-- | core/object/worker_thread_pool.h | 198 |
17 files changed, 1211 insertions, 734 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/callable_method_pointer.h b/core/object/callable_method_pointer.h index 577d4b9fbd..f2a440b49a 100644 --- a/core/object/callable_method_pointer.h +++ b/core/object/callable_method_pointer.h @@ -245,4 +245,86 @@ Callable create_custom_callable_function_pointer(T *p_instance, #define callable_mp(I, M) create_custom_callable_function_pointer(I, M) #endif +// STATIC VERSIONS + +template <class... P> +class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase { + struct Data { + void (*method)(P...); + } data; + +public: + virtual ObjectID get_object() const { + return ObjectID(); + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { + call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); + r_return_value = Variant(); + } + + CallableCustomStaticMethodPointer(void (*p_method)(P...)) { + memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. + data.method = p_method; + _setup((uint32_t *)&data, sizeof(Data)); + } +}; + +template <class T, class... P> +Callable create_custom_callable_static_function_pointer( +#ifdef DEBUG_METHODS_ENABLED + const char *p_func_text, +#endif + void (*p_method)(P...)) { + typedef CallableCustomStaticMethodPointer<P...> CCMP; // Messes with memnew otherwise. + CCMP *ccmp = memnew(CCMP(p_method)); +#ifdef DEBUG_METHODS_ENABLED + ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. +#endif + return Callable(ccmp); +} + +template <class R, class... P> +class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase { + struct Data { + R(*method) + (P...); + } data; + +public: + virtual ObjectID get_object() const { + return ObjectID(); + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { + call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); + } + + CallableCustomStaticMethodPointerRet(R (*p_method)(P...)) { + memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. + data.method = p_method; + _setup((uint32_t *)&data, sizeof(Data)); + } +}; + +template <class R, class... P> +Callable create_custom_callable_static_function_pointer( +#ifdef DEBUG_METHODS_ENABLED + const char *p_func_text, +#endif + R (*p_method)(P...)) { + typedef CallableCustomStaticMethodPointerRet<R, P...> CCMP; // Messes with memnew otherwise. + CCMP *ccmp = memnew(CCMP(p_method)); +#ifdef DEBUG_METHODS_ENABLED + ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. +#endif + return Callable(ccmp); +} + +#ifdef DEBUG_METHODS_ENABLED +#define callable_mp_static(M) create_custom_callable_static_function_pointer(#M, M) +#else +#define callable_mp_static(M) create_custom_callable_static_function_pointer(M) +#endif + #endif // CALLABLE_METHOD_POINTER_H diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 61ce965bc3..d67315f20d 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -39,190 +39,13 @@ #ifdef DEBUG_METHODS_ENABLED -MethodDefinition D_METHOD(const char *p_name) { +MethodDefinition D_METHODP(const char *p_name, const char *const **p_args, uint32_t p_argcount) { MethodDefinition md; md.name = StaticCString::create(p_name); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.push_back(StaticCString::create(p_arg1)); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(2); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(3); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(4); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(5); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(6); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(7); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(8); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - md.args.write[7] = StaticCString::create(p_arg8); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(9); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - md.args.write[7] = StaticCString::create(p_arg8); - md.args.write[8] = StaticCString::create(p_arg9); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(10); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - md.args.write[7] = StaticCString::create(p_arg8); - md.args.write[8] = StaticCString::create(p_arg9); - md.args.write[9] = StaticCString::create(p_arg10); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(11); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - md.args.write[7] = StaticCString::create(p_arg8); - md.args.write[8] = StaticCString::create(p_arg9); - md.args.write[9] = StaticCString::create(p_arg10); - md.args.write[10] = StaticCString::create(p_arg11); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(12); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - md.args.write[7] = StaticCString::create(p_arg8); - md.args.write[8] = StaticCString::create(p_arg9); - md.args.write[9] = StaticCString::create(p_arg10); - md.args.write[10] = StaticCString::create(p_arg11); - md.args.write[11] = StaticCString::create(p_arg12); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12, const char *p_arg13) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(13); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - md.args.write[7] = StaticCString::create(p_arg8); - md.args.write[8] = StaticCString::create(p_arg9); - md.args.write[9] = StaticCString::create(p_arg10); - md.args.write[10] = StaticCString::create(p_arg11); - md.args.write[11] = StaticCString::create(p_arg12); - md.args.write[12] = StaticCString::create(p_arg13); + md.args.resize(p_argcount); + for (uint32_t i = 0; i < p_argcount; i++) { + md.args.write[i] = StaticCString::create(*p_args[i]); + } return md; } @@ -267,10 +90,8 @@ bool ClassDB::is_parent_class(const StringName &p_class, const StringName &p_inh void ClassDB::get_class_list(List<StringName> *p_classes) { OBJTYPE_RLOCK; - const StringName *k = nullptr; - - while ((k = classes.next(k))) { - p_classes->push_back(*k); + for (const KeyValue<StringName, ClassInfo> &E : classes) { + p_classes->push_back(E.key); } p_classes->sort(); @@ -279,11 +100,9 @@ void ClassDB::get_class_list(List<StringName> *p_classes) { void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) { OBJTYPE_RLOCK; - const StringName *k = nullptr; - - while ((k = classes.next(k))) { - if (*k != p_class && _is_parent_class(*k, p_class)) { - p_classes->push_back(*k); + for (const KeyValue<StringName, ClassInfo> &E : classes) { + if (E.key != p_class && _is_parent_class(E.key, p_class)) { + p_classes->push_back(E.key); } } } @@ -291,11 +110,9 @@ void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringNa void ClassDB::get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) { OBJTYPE_RLOCK; - const StringName *k = nullptr; - - while ((k = classes.next(k))) { - if (*k != p_class && _get_parent_class(*k) == p_class) { - p_classes->push_back(*k); + for (const KeyValue<StringName, ClassInfo> &E : classes) { + if (E.key != p_class && _get_parent_class(E.key) == p_class) { + p_classes->push_back(E.key); } } } @@ -347,35 +164,28 @@ 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> names; - - const StringName *k = nullptr; - - while ((k = classes.next(k))) { - names.push_back(*k); - } - //must be alphabetically sorted for hash to compute - names.sort_custom<StringName::AlphCompare>(); + List<StringName> class_list; + ClassDB::get_class_list(&class_list); + // Must be alphabetically sorted for hash to compute. + class_list.sort_custom<StringName::AlphCompare>(); - for (const StringName &E : names) { + for (const StringName &E : class_list) { ClassInfo *t = classes.getptr(E); ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E) + "'."); 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 List<StringName> snames; - k = nullptr; - - while ((k = t->method_map.next(k))) { - String name = k->operator String(); + for (const KeyValue<StringName, MethodBind *> &F : t->method_map) { + String name = F.key.operator String(); ERR_CONTINUE(name.is_empty()); @@ -383,34 +193,34 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { continue; // Ignore non-virtual methods that start with an underscore } - snames.push_back(*k); + snames.push_back(F.key); } snames.sort_custom<StringName::AlphCompare>(); 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); } } @@ -418,17 +228,15 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { List<StringName> snames; - k = nullptr; - - while ((k = t->constant_map.next(k))) { - snames.push_back(*k); + 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); } } @@ -436,19 +244,17 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { List<StringName> snames; - k = nullptr; - - while ((k = t->signal_map.next(k))) { - snames.push_back(*k); + for (const KeyValue<StringName, MethodInfo> &F : t->signal_map) { + snames.push_back(F.key); } snames.sort_custom<StringName::AlphCompare>(); 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); } } } @@ -457,10 +263,8 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { List<StringName> snames; - k = nullptr; - - while ((k = t->property_setget.next(k))) { - snames.push_back(*k); + for (const KeyValue<StringName, PropertySetGet> &F : t->property_setget) { + snames.push_back(F.key); } snames.sort_custom<StringName::AlphCompare>(); @@ -469,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 @@ -501,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; { @@ -651,10 +462,8 @@ void ClassDB::get_method_list(const StringName &p_class, List<MethodInfo> *p_met #else - const StringName *K = nullptr; - - while ((K = type->method_map.next(K))) { - MethodBind *m = type->method_map[*K]; + for (KeyValue<StringName, MethodBind *> &E : type->method_map) { + MethodBind *m = E.value; MethodInfo minfo = info_from_bind(m); p_methods->push_back(minfo); } @@ -734,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); @@ -753,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; } } @@ -780,10 +591,9 @@ void ClassDB::get_integer_constant_list(const StringName &p_class, List<String> p_constants->push_back(E); } #else - const StringName *K = nullptr; - while ((K = type->constant_map.next(K))) { - p_constants->push_back(*K); + for (const KeyValue<StringName, int64_t> &E : type->constant_map) { + p_constants->push_back(E.key); } #endif @@ -795,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; @@ -844,12 +654,11 @@ StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const S ClassInfo *type = classes.getptr(p_class); while (type) { - const StringName *k = nullptr; - while ((k = type->enum_map.next(k))) { - List<StringName> &constants_list = type->enum_map.get(*k); + 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 *k; + return E.key; } } @@ -869,9 +678,8 @@ void ClassDB::get_enum_list(const StringName &p_class, List<StringName> *p_enums ClassInfo *type = classes.getptr(p_class); while (type) { - const StringName *k = nullptr; - while ((k = type->enum_map.next(k))) { - p_enums->push_back(*k); + for (KeyValue<StringName, ClassInfo::EnumInfo> &E : type->enum_map) { + p_enums->push_back(E.key); } if (p_no_inheritance) { @@ -888,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()); } } @@ -949,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; @@ -977,9 +804,8 @@ void ClassDB::get_signal_list(const StringName &p_class, List<MethodInfo> *p_sig ClassInfo *check = type; while (check) { - const StringName *S = nullptr; - while ((S = check->signal_map.next(S))) { - p_signals->push_back(check->signal_map[*S]); + for (KeyValue<StringName, MethodInfo> &E : check->signal_map) { + p_signals->push_back(E.value); } if (p_no_inheritance) { @@ -1268,7 +1094,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; @@ -1574,10 +1400,8 @@ void ClassDB::add_resource_base_extension(const StringName &p_extension, const S } void ClassDB::get_resource_base_extensions(List<String> *p_extensions) { - const StringName *K = nullptr; - - while ((K = resource_base_extensions.next(K))) { - p_extensions->push_back(*K); + for (const KeyValue<StringName, StringName> &E : resource_base_extensions) { + p_extensions->push_back(E.key); } } @@ -1586,18 +1410,15 @@ bool ClassDB::is_resource_extension(const StringName &p_extension) { } void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p_extensions) { - const StringName *K = nullptr; - - while ((K = resource_base_extensions.next(K))) { - StringName cmp = resource_base_extensions[*K]; - if (is_parent_class(p_class, cmp) || is_parent_class(cmp, p_class)) { - p_extensions->push_back(*K); + for (const KeyValue<StringName, StringName> &E : resource_base_extensions) { + if (is_parent_class(p_class, E.value) || is_parent_class(E.value, p_class)) { + p_extensions->push_back(E.key); } } } HashMap<StringName, HashMap<StringName, Variant>> ClassDB::default_values; -Set<StringName> ClassDB::default_values_cached; +HashSet<StringName> ClassDB::default_values_cached; Variant ClassDB::class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid) { if (!default_values_cached.has(p_class)) { @@ -1699,7 +1520,7 @@ void ClassDB::unregister_extension_class(const StringName &p_class) { classes.erase(p_class); } -Map<StringName, ClassDB::NativeStruct> ClassDB::native_structs; +HashMap<StringName, ClassDB::NativeStruct> ClassDB::native_structs; void ClassDB::register_native_struct(const StringName &p_name, const String &p_code, uint64_t p_current_size) { NativeStruct ns; ns.ccode = p_code; @@ -1733,14 +1554,11 @@ void ClassDB::cleanup_defaults() { void ClassDB::cleanup() { //OBJTYPE_LOCK; hah not here - const StringName *k = nullptr; - - while ((k = classes.next(k))) { - ClassInfo &ti = classes[*k]; + for (KeyValue<StringName, ClassInfo> &E : classes) { + ClassInfo &ti = E.value; - const StringName *m = nullptr; - while ((m = ti.method_map.next(m))) { - memdelete(ti.method_map[*m]); + for (KeyValue<StringName, MethodBind *> &F : ti.method_map) { + memdelete(F.value); } } classes.clear(); diff --git a/core/object/class_db.h b/core/object/class_db.h index 333a3307e2..8b6a260d86 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -35,13 +35,10 @@ #include "core/object/object.h" #include "core/string/print_string.h" -/** To bind more then 6 parameters include this: - * - */ - // Makes callable_mp readily available in all classes connecting signals. // Needs to come after method_bind and object have been included. #include "core/object/callable_method_pointer.h" +#include "core/templates/hash_set.h" #define DEFVAL(m_defval) (m_defval) @@ -57,20 +54,18 @@ struct MethodDefinition { name(p_name) {} }; -MethodDefinition D_METHOD(const char *p_name); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12, const char *p_arg13); +MethodDefinition D_METHODP(const char *p_name, const char *const **p_args, uint32_t p_argcount); + +template <typename... VarArgs> +MethodDefinition D_METHOD(const char *p_name, const VarArgs... p_args) { + const char *args[sizeof...(p_args) + 1] = { p_args..., nullptr }; // +1 makes sure zero sized arrays are also supported. + const char *const *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + + return D_METHODP(p_name, sizeof...(p_args) == 0 ? nullptr : (const char *const **)argptrs, sizeof...(p_args)); +} #else @@ -108,18 +103,23 @@ 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; #ifdef DEBUG_METHODS_ENABLED List<StringName> constant_order; List<StringName> method_order; - Set<StringName> methods_in_properties; + HashSet<StringName> methods_in_properties; List<MethodInfo> virtual_methods; - Map<StringName, MethodInfo> virtual_methods_map; - Map<StringName, Vector<Error>> method_error_values; + HashMap<StringName, MethodInfo> virtual_methods_map; + HashMap<StringName, Vector<Error>> method_error_values; #endif HashMap<StringName, PropertySetGet> property_setget; @@ -155,14 +155,14 @@ public: static void _add_class2(const StringName &p_class, const StringName &p_inherits); static HashMap<StringName, HashMap<StringName, Variant>> default_values; - static Set<StringName> default_values_cached; + static HashSet<StringName> default_values_cached; // Native structs, used by binder struct NativeStruct { String ccode; // C code to create the native struct, fields separated by ; Arrays accepted (even containing other structs), also function pointers. All types must be Godot types. uint64_t struct_size; // local size of struct, for comparison }; - static Map<StringName, NativeStruct> native_structs; + static HashMap<StringName, NativeStruct> native_structs; private: // Non-locking variants of get_parent_class and is_parent_class. @@ -330,15 +330,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); @@ -355,6 +357,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(); @@ -375,6 +378,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) { } @@ -406,20 +412,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 79c36ac81f..13dc921c9f 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -142,9 +142,9 @@ Error MessageQueue::push_callablep(const Callable &p_callable, const Variant **p } void MessageQueue::statistics() { - Map<StringName, int> set_count; - Map<int, int> notify_count; - Map<Callable, int> call_count; + HashMap<StringName, int> set_count; + HashMap<int, int> notify_count; + HashMap<Callable, int> call_count; int null_count = 0; uint32_t read_pos = 0; @@ -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 897b5d18de..75dbe8872f 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() { @@ -417,9 +258,9 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid return; } else { - OrderedHashMap<StringName, Variant>::Element *E = metadata_properties.getptr(p_name); - if (E) { - E->get() = p_value; + Variant **V = metadata_properties.getptr(p_name); + if (V) { + **V = p_value; if (r_valid) { *r_valid = true; } @@ -434,15 +275,6 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid } } - // Something inside the object... :| - bool success = _setv(p_name, p_value); - if (success) { - if (r_valid) { - *r_valid = true; - } - return; - } - #ifdef TOOLS_ENABLED if (script_instance) { bool valid; @@ -456,6 +288,15 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid } #endif + // Something inside the object... :| + bool success = _setv(p_name, p_value); + if (success) { + if (r_valid) { + *r_valid = true; + } + return; + } + if (r_valid) { *r_valid = false; } @@ -508,25 +349,16 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { return ret; } - const OrderedHashMap<StringName, Variant>::Element *E = metadata_properties.getptr(p_name); + const Variant *const *V = metadata_properties.getptr(p_name); - if (E) { - ret = E->get(); + if (V) { + ret = **V; if (r_valid) { *r_valid = true; } return ret; } else { - // Something inside the object... :| - bool success = _getv(p_name, ret); - if (success) { - if (r_valid) { - *r_valid = true; - } - return ret; - } - #ifdef TOOLS_ENABLED if (script_instance) { bool valid; @@ -539,6 +371,14 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { } } #endif + // Something inside the object... :| + bool success = _getv(p_name, ret); + if (success) { + if (r_valid) { + *r_valid = true; + } + return ret; + } if (r_valid) { *r_valid = false; @@ -649,7 +489,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); @@ -667,9 +507,9 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons script_instance->get_property_list(p_list); } - for (OrderedHashMap<StringName, Variant>::ConstElement K = metadata.front(); K; K = K.next()) { - PropertyInfo pi = PropertyInfo(K.value().get_type(), "metadata/" + K.key().operator String()); - if (K.value().get_type() == Variant::OBJECT) { + for (const KeyValue<StringName, Variant> &K : metadata) { + PropertyInfo pi = PropertyInfo(K.value.get_type(), "metadata/" + K.key.operator String()); + if (K.value.get_type() == Variant::OBJECT) { pi.hint = PROPERTY_HINT_RESOURCE_TYPE; pi.hint_string = "Resource"; } @@ -825,6 +665,7 @@ 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: { } @@ -844,6 +685,54 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_ 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: { + } + } + } + + //extension does not need this, because all methods are registered in MethodBind + + 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; + } + + return ret; +} + void Object::notification(int p_notification, bool p_reversed) { _notificationv(p_notification, p_reversed); @@ -945,13 +834,13 @@ void Object::set_meta(const StringName &p_name, const Variant &p_value) { return; } - OrderedHashMap<StringName, Variant>::Element E = metadata.find(p_name); + HashMap<StringName, Variant>::Iterator E = metadata.find(p_name); if (E) { - E.value() = p_value; + E->value = p_value; } else { ERR_FAIL_COND(!p_name.operator String().is_valid_identifier()); - E = metadata.insert(p_name, p_value); - metadata_properties["metadata/" + p_name.operator String()] = E; + Variant *V = &metadata.insert(p_name, p_value)->value; + metadata_properties["metadata/" + p_name.operator String()] = V; notify_property_list_changed(); } } @@ -994,16 +883,16 @@ Array Object::_get_method_list_bind() const { Vector<StringName> Object::_get_meta_list_bind() const { Vector<StringName> _metaret; - for (OrderedHashMap<StringName, Variant>::ConstElement K = metadata.front(); K; K = K.next()) { - _metaret.push_back(K.key()); + for (const KeyValue<StringName, Variant> &K : metadata) { + _metaret.push_back(K.key); } return _metaret; } void Object::get_meta_list(List<StringName> *p_list) const { - for (OrderedHashMap<StringName, Variant>::ConstElement K = metadata.front(); K; K = K.next()) { - p_list->push_back(K.key()); + for (const KeyValue<StringName, Variant> &K : metadata) { + p_list->push_back(K.key); } } @@ -1080,8 +969,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++) { @@ -1096,28 +983,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) { @@ -1197,7 +1069,7 @@ Array Object::_get_signal_list() const { return ret; } -Array Object::_get_signal_connection_list(const String &p_signal) const { +Array Object::_get_signal_connection_list(const StringName &p_signal) const { List<Connection> conns; get_all_signal_connections(&conns); @@ -1251,21 +1123,18 @@ void Object::get_signal_list(List<MethodInfo> *p_signals) const { ClassDB::get_signal_list(get_class_name(), p_signals); //find maybe usersignals? - const StringName *S = nullptr; - while ((S = signal_map.next(S))) { - if (!signal_map[*S].user.name.is_empty()) { + for (const KeyValue<StringName, SignalData> &E : signal_map) { + if (!E.value.user.name.is_empty()) { //user signal - p_signals->push_back(signal_map[*S].user); + p_signals->push_back(E.value.user); } } } void Object::get_all_signal_connections(List<Connection> *p_connections) const { - const StringName *S = nullptr; - - while ((S = signal_map.next(S))) { - const SignalData *s = &signal_map[*S]; + for (const KeyValue<StringName, SignalData> &E : signal_map) { + const SignalData *s = &E.value; for (int i = 0; i < s->slot_map.size(); i++) { p_connections->push_back(s->slot_map.getv(i).conn); @@ -1286,10 +1155,9 @@ void Object::get_signal_connection_list(const StringName &p_signal, List<Connect int Object::get_persistent_signal_connection_count() const { int count = 0; - const StringName *S = nullptr; - while ((S = signal_map.next(S))) { - const SignalData *s = &signal_map[*S]; + for (const KeyValue<StringName, SignalData> &E : signal_map) { + const SignalData *s = &E.value; for (int i = 0; i < s->slot_map.size(); i++) { if (s->slot_map.getv(i).conn.flags & CONNECT_PERSIST) { @@ -1307,7 +1175,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(); @@ -1355,7 +1223,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) { @@ -1387,8 +1254,6 @@ bool Object::is_connected(const StringName &p_signal, const Callable &p_callable Callable target = p_callable; return s->slot_map.has(*target.get_base_comparator()); - //const Map<Signal::Target,Signal::Slot>::Element *E = s->slot_map.find(target); - //return (E!=nullptr ); } void Object::disconnect(const StringName &p_signal, const Callable &p_callable) { @@ -1429,11 +1294,11 @@ void Object::_disconnect(const StringName &p_signal, const Callable &p_callable, } } -void Object::_set_bind(const String &p_set, const Variant &p_value) { +void Object::_set_bind(const StringName &p_set, const Variant &p_value) { set(p_set, p_value); } -Variant Object::_get_bind(const String &p_name) const { +Variant Object::_get_bind(const StringName &p_name) const { return get(p_name); } @@ -1476,7 +1341,7 @@ String Object::tr_n(const StringName &p_message, const StringName &p_message_plu void Object::_clear_internal_resource_paths(const Variant &p_var) { switch (p_var.get_type()) { case Variant::OBJECT: { - RES r = p_var; + Ref<Resource> r = p_var; if (!r.is_valid()) { return; } @@ -1605,7 +1470,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); @@ -1867,15 +1732,15 @@ Object::~Object() { _extension_instance = nullptr; } - const StringName *S = nullptr; - if (_emitting) { //@todo this may need to actually reach the debugger prioritarily somehow because it may crash before ERR_PRINT("Object " + to_string() + " was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes."); } - while ((S = signal_map.next(nullptr))) { - SignalData *s = &signal_map[*S]; + while (signal_map.size()) { + // Avoid regular iteration so erasing is safe. + KeyValue<StringName, SignalData> &E = *signal_map.begin(); + SignalData *s = &E.value; //brute force disconnect for performance int slot_count = s->slot_map.size(); @@ -1885,7 +1750,7 @@ Object::~Object() { slot_list[i].value.conn.callable.get_object()->connections.erase(slot_list[i].value.cE); } - signal_map.erase(*S); + signal_map.erase(E.key); } //signals from nodes that connect to this node diff --git a/core/object/object.h b/core/object/object.h index c3e3c68b59..f3f89982d9 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -37,23 +37,21 @@ #include "core/os/rw_lock.h" #include "core/os/spin_lock.h" #include "core/templates/hash_map.h" +#include "core/templates/hash_set.h" #include "core/templates/list.h" -#include "core/templates/map.h" -#include "core/templates/ordered_hash_map.h" +#include "core/templates/rb_map.h" #include "core/templates/safe_refcount.h" -#include "core/templates/set.h" #include "core/templates/vmap.h" #include "core/variant/callable_bind.h" #include "core/variant/variant.h" 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_KEY_ACCEL, ///< hint_text= "length" (as integer) + PROPERTY_HINT_LINK, 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) @@ -187,6 +187,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 +211,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 +237,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 +358,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 +510,6 @@ public: Callable callable; uint32_t flags = 0; - Vector<Variant> binds; bool operator<(const Connection &p_conn) const; operator Variant() const; @@ -511,12 +554,12 @@ private: #ifdef TOOLS_ENABLED bool _edited = false; uint32_t _edited_version = 0; - Set<String> editor_section_folding; + HashSet<String> editor_section_folding; #endif ScriptInstance *script_instance = nullptr; Variant script; // Reference does not exist yet, store it in a Variant. - OrderedHashMap<StringName, Variant> metadata; - HashMap<StringName, OrderedHashMap<StringName, Variant>::Element> metadata_properties; + HashMap<StringName, Variant> metadata; + HashMap<StringName, Variant *> metadata_properties; mutable StringName _class_name; mutable const StringName *_class_ptr = nullptr; @@ -524,10 +567,10 @@ private: bool _has_user_signal(const StringName &p_name) const; Error _emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Array _get_signal_list() const; - Array _get_signal_connection_list(const String &p_signal) const; + Array _get_signal_connection_list(const StringName &p_signal) const; Array _get_incoming_connections() const; - void _set_bind(const String &p_set, const Variant &p_value); - Variant _get_bind(const String &p_name) const; + void _set_bind(const StringName &p_set, const Variant &p_value); + Variant _get_bind(const StringName &p_name) const; void _set_indexed_bind(const NodePath &p_name, const Variant &p_value); Variant _get_indexed_bind(const NodePath &p_name) const; @@ -624,6 +667,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 +764,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 +828,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; @@ -816,7 +862,7 @@ public: #ifdef TOOLS_ENABLED void editor_set_section_unfold(const String &p_section, bool p_unfolded); bool editor_is_section_unfolded(const String &p_section); - const Set<String> &editor_get_section_folding() const { return editor_section_folding; } + const HashSet<String> &editor_get_section_folding() const { return editor_section_folding; } void editor_clear_section_folding() { editor_section_folding.clear(); } #endif diff --git a/core/object/ref_counted.cpp b/core/object/ref_counted.cpp index c9a7b2a608..726e2c012c 100644 --- a/core/object/ref_counted.cpp +++ b/core/object/ref_counted.cpp @@ -108,7 +108,7 @@ Variant WeakRef::get_ref() const { } RefCounted *r = cast_to<RefCounted>(obj); if (r) { - return REF(r); + return Ref<RefCounted>(r); } return obj; @@ -118,7 +118,7 @@ void WeakRef::set_obj(Object *p_object) { ref = p_object ? p_object->get_instance_id() : ObjectID(); } -void WeakRef::set_ref(const REF &p_ref) { +void WeakRef::set_ref(const Ref<RefCounted> &p_ref) { ref = p_ref.is_valid() ? p_ref->get_instance_id() : ObjectID(); } diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h index dcacf19890..bd06a84bd8 100644 --- a/core/object/ref_counted.h +++ b/core/object/ref_counted.h @@ -234,8 +234,6 @@ public: } }; -typedef Ref<RefCounted> REF; - class WeakRef : public RefCounted { GDCLASS(WeakRef, RefCounted); @@ -247,7 +245,7 @@ protected: public: Variant get_ref() const; void set_obj(Object *p_object); - void set_ref(const REF &p_ref); + void set_ref(const Ref<RefCounted> &p_ref); WeakRef() {} }; diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index a5d25bf533..226fd8b791 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -93,7 +93,7 @@ Array Script::_get_script_signal_list() { Dictionary Script::_get_script_constant_map() { Dictionary ret; - Map<StringName, Variant> map; + HashMap<StringName, Variant> map; get_constants(&map); for (const KeyValue<StringName, Variant> &E : map) { ret[E.key] = E.value; @@ -253,10 +253,9 @@ StringName ScriptServer::get_global_class_native_base(const String &p_class) { } void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) { - const StringName *K = nullptr; List<StringName> classes; - while ((K = global_classes.next(K))) { - classes.push_back(*K); + for (const KeyValue<StringName, GlobalScriptClass> &E : global_classes) { + classes.push_back(E.key); } classes.sort_custom<StringName::AlphCompare>(); for (const StringName &E : classes) { @@ -296,6 +295,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); @@ -340,11 +344,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"); @@ -475,8 +482,8 @@ bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { return false; } -void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values) { - Set<StringName> new_values; +void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const HashMap<StringName, Variant> &p_values) { + HashSet<StringName> new_values; for (const PropertyInfo &E : p_properties) { StringName n = E.name; new_values.insert(n); @@ -491,16 +498,16 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c properties = p_properties; List<StringName> to_remove; - for (Map<StringName, Variant>::Element *E = values.front(); E; E = E->next()) { - if (!new_values.has(E->key())) { - to_remove.push_back(E->key()); + for (KeyValue<StringName, Variant> &E : values) { + if (!new_values.has(E.key)) { + to_remove.push_back(E.key); } Variant defval; - if (script->get_property_default_value(E->key(), defval)) { + if (script->get_property_default_value(E.key, defval)) { //remove because it's the same as the default value - if (defval == E->get()) { - to_remove.push_back(E->key()); + if (defval == E.value) { + to_remove.push_back(E.key); } } } @@ -521,10 +528,10 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid) { if (script->is_placeholder_fallback_enabled()) { - Map<StringName, Variant>::Element *E = values.find(p_name); + HashMap<StringName, Variant>::Iterator E = values.find(p_name); if (E) { - E->value() = p_value; + E->value = p_value; } else { values.insert(p_name, p_value); } @@ -548,13 +555,13 @@ void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name, Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_name, bool *r_valid) { if (script->is_placeholder_fallback_enabled()) { - const Map<StringName, Variant>::Element *E = values.find(p_name); + HashMap<StringName, Variant>::ConstIterator E = values.find(p_name); if (E) { if (r_valid) { *r_valid = true; } - return E->value(); + return E->value; } E = constants.find(p_name); @@ -562,7 +569,7 @@ Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_nam if (r_valid) { *r_valid = true; } - return E->value(); + return E->value; } } diff --git a/core/object/script_language.h b/core/object/script_language.h index 69002c81f4..c9f8a4f828 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -33,9 +33,8 @@ #include "core/doc_data.h" #include "core/io/resource.h" -#include "core/multiplayer/multiplayer.h" -#include "core/templates/map.h" #include "core/templates/pair.h" +#include "core/templates/rb_map.h" class ScriptLanguage; @@ -154,12 +153,12 @@ public: virtual int get_member_line(const StringName &p_member) const { return -1; } - virtual void get_constants(Map<StringName, Variant> *p_constants) {} - virtual void get_members(Set<StringName> *p_constants) {} + virtual void get_constants(HashMap<StringName, Variant> *p_constants) {} + virtual void get_members(HashSet<StringName> *p_constants) {} 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 +189,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 +212,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(); @@ -283,7 +283,7 @@ public: virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { return Ref<Script>(); } virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) { return Vector<ScriptTemplate>(); } virtual bool is_using_templates() { return false; } - virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const = 0; + virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const = 0; virtual String validate_path(const String &p_path) const { return ""; } virtual Script *create_script() const = 0; virtual bool has_named_classes() const = 0; @@ -323,7 +323,7 @@ public: String display; String insert_text; Color font_color; - RES icon; + Ref<Resource> icon; Variant default_value; Vector<Pair<int, int>> matches; int location = LOCATION_OTHER; @@ -346,8 +346,10 @@ public: LOOKUP_RESULT_CLASS_CONSTANT, LOOKUP_RESULT_CLASS_PROPERTY, LOOKUP_RESULT_CLASS_METHOD, + LOOKUP_RESULT_CLASS_SIGNAL, LOOKUP_RESULT_CLASS_ENUM, LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE, + LOOKUP_RESULT_CLASS_ANNOTATION, LOOKUP_RESULT_MAX }; @@ -400,6 +402,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; @@ -432,8 +435,8 @@ extern uint8_t script_encryption_key[32]; class PlaceHolderScriptInstance : public ScriptInstance { Object *owner = nullptr; List<PropertyInfo> properties; - Map<StringName, Variant> values; - Map<StringName, Variant> constants; + HashMap<StringName, Variant> values; + HashMap<StringName, Variant> constants; ScriptLanguage *language = nullptr; Ref<Script> script; @@ -458,14 +461,14 @@ public: Object *get_owner() override { return owner; } - void update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values); //likely changed in editor + void update(const List<PropertyInfo> &p_properties, const HashMap<StringName, Variant> &p_values); //likely changed in editor virtual bool is_placeholder() const override { return true; } 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 21d7685674..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); @@ -157,8 +158,10 @@ void ScriptLanguageExtension::_bind_methods() { BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_CONSTANT); BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_PROPERTY); BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_METHOD); + 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 40f18ab30d..10eacfd9f7 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -153,7 +153,7 @@ public: GDVIRTUAL0RC(Dictionary, _get_constants) - virtual void get_constants(Map<StringName, Variant> *p_constants) override { + virtual void get_constants(HashMap<StringName, Variant> *p_constants) override { Dictionary constants; GDVIRTUAL_REQUIRED_CALL(_get_constants, constants); List<Variant> keys; @@ -163,7 +163,7 @@ public: } } GDVIRTUAL0RC(TypedArray<StringName>, _get_members) - virtual void get_members(Set<StringName> *p_members) override { + virtual void get_members(HashSet<StringName> *p_members) override { TypedArray<StringName> members; GDVIRTUAL_REQUIRED_CALL(_get_members, members); for (int i = 0; i < members.size(); i++) { @@ -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() {} @@ -282,7 +266,7 @@ public: EXBIND0R(bool, is_using_templates) GDVIRTUAL6RC(Dictionary, _validate, const String &, const String &, bool, bool, bool, bool) - virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override { + virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override { Dictionary ret; GDVIRTUAL_REQUIRED_CALL(_validate, p_script, p_path, r_functions != nullptr, r_errors != nullptr, r_warnings != nullptr, r_safe_lines != nullptr, ret); if (!ret.has("valid")) { @@ -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) @@ -671,7 +664,7 @@ public: uint32_t pcount; const GDNativePropertyInfo *pinfo = native_info->get_property_list_func(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 (native_info->free_property_list_func) { native_info->free_property_list_func(instance, pinfo); @@ -716,9 +709,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 |