summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript.cpp')
-rw-r--r--modules/gdscript/gdscript.cpp285
1 files changed, 173 insertions, 112 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 4accdb4d21..a999acd1bd 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1117,8 +1117,7 @@ GDScript *GDScript::find_class(const String &p_qualified_name) {
// Starts at index 1 because index 0 was handled above.
for (int i = 1; result != nullptr && i < class_names.size(); i++) {
- String current_name = class_names[i];
- if (HashMap<StringName, Ref<GDScript>>::Iterator E = result->subclasses.find(current_name)) {
+ if (HashMap<StringName, Ref<GDScript>>::Iterator E = result->subclasses.find(class_names[i])) {
result = E->value.ptr();
} else {
// Couldn't find inner class.
@@ -1156,8 +1155,8 @@ RBSet<GDScript *> GDScript::get_dependencies() {
return dependencies;
}
-RBSet<GDScript *> GDScript::get_inverted_dependencies() {
- RBSet<GDScript *> inverted_dependencies;
+HashMap<GDScript *, RBSet<GDScript *>> GDScript::get_all_dependencies() {
+ HashMap<GDScript *, RBSet<GDScript *>> all_dependencies;
List<GDScript *> scripts;
{
@@ -1171,51 +1170,42 @@ RBSet<GDScript *> GDScript::get_inverted_dependencies() {
}
for (GDScript *scr : scripts) {
- if (scr == nullptr || scr == this || scr->destructing) {
+ if (scr == nullptr || scr->destructing) {
continue;
}
-
- RBSet<GDScript *> scr_dependencies = scr->get_dependencies();
- if (scr_dependencies.has(this)) {
- inverted_dependencies.insert(scr);
- }
+ all_dependencies.insert(scr, scr->get_dependencies());
}
- return inverted_dependencies;
+ return all_dependencies;
}
RBSet<GDScript *> GDScript::get_must_clear_dependencies() {
RBSet<GDScript *> dependencies = get_dependencies();
RBSet<GDScript *> must_clear_dependencies;
- HashMap<GDScript *, RBSet<GDScript *>> inverted_dependencies;
-
- for (GDScript *E : dependencies) {
- inverted_dependencies.insert(E, E->get_inverted_dependencies());
- }
+ HashMap<GDScript *, RBSet<GDScript *>> all_dependencies = get_all_dependencies();
RBSet<GDScript *> cant_clear;
- for (KeyValue<GDScript *, RBSet<GDScript *>> &E : inverted_dependencies) {
+ for (KeyValue<GDScript *, RBSet<GDScript *>> &E : all_dependencies) {
+ if (dependencies.has(E.key)) {
+ continue;
+ }
for (GDScript *F : E.value) {
- if (!dependencies.has(F)) {
- cant_clear.insert(E.key);
- for (GDScript *G : E.key->get_dependencies()) {
- cant_clear.insert(G);
- }
- break;
+ if (dependencies.has(F)) {
+ cant_clear.insert(F);
}
}
}
- for (KeyValue<GDScript *, RBSet<GDScript *>> &E : inverted_dependencies) {
- if (cant_clear.has(E.key) || ScriptServer::is_global_class(E.key->get_fully_qualified_name())) {
+ for (GDScript *E : dependencies) {
+ if (cant_clear.has(E) || ScriptServer::is_global_class(E->get_fully_qualified_name())) {
continue;
}
- must_clear_dependencies.insert(E.key);
+ must_clear_dependencies.insert(E);
}
cant_clear.clear();
dependencies.clear();
- inverted_dependencies.clear();
+ all_dependencies.clear();
return must_clear_dependencies;
}
@@ -1391,106 +1381,51 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) {
}
#endif
-thread_local GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_thread_local;
-GDScript::UpdatableFuncPtr *GDScript::func_ptrs_to_update_main_thread = &func_ptrs_to_update_thread_local;
+GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_main_thread;
+thread_local GDScript::UpdatableFuncPtr *GDScript::func_ptrs_to_update_thread_local = nullptr;
-GDScript::UpdatableFuncPtrElement *GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) {
- MutexLock lock(func_ptrs_to_update_mutex);
-
- List<UpdatableFuncPtrElement>::Element *result = func_ptrs_to_update_elems.push_back(UpdatableFuncPtrElement());
+GDScript::UpdatableFuncPtrElement GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) {
+ UpdatableFuncPtrElement result = {};
{
- MutexLock lock2(func_ptrs_to_update_thread_local.mutex);
- result->get().element = func_ptrs_to_update_thread_local.ptrs.push_back(p_func_ptr_ptr);
- result->get().mutex = &func_ptrs_to_update_thread_local.mutex;
+ MutexLock lock(func_ptrs_to_update_thread_local->mutex);
+ result.element = func_ptrs_to_update_thread_local->ptrs.push_back(p_func_ptr_ptr);
+ result.func_ptr = func_ptrs_to_update_thread_local;
- if (likely(func_ptrs_to_update_thread_local.initialized)) {
- return &result->get();
+ if (likely(func_ptrs_to_update_thread_local->initialized)) {
+ return result;
}
- func_ptrs_to_update_thread_local.initialized = true;
+ func_ptrs_to_update_thread_local->initialized = true;
}
- func_ptrs_to_update.push_back(&func_ptrs_to_update_thread_local);
+ MutexLock lock(func_ptrs_to_update_mutex);
+ func_ptrs_to_update.push_back(func_ptrs_to_update_thread_local);
+ func_ptrs_to_update_thread_local->rc++;
- return &result->get();
+ return result;
}
-void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement *p_func_ptr_element) {
- // None of these checks should ever fail, unless there's a bug.
- // They can be removed once we are sure they never catch anything.
- // Left here now due to extra safety needs late in the release cycle.
- ERR_FAIL_NULL(p_func_ptr_element);
- MutexLock lock(*p_func_ptr_element->mutex);
- ERR_FAIL_NULL(p_func_ptr_element->element);
- ERR_FAIL_NULL(p_func_ptr_element->mutex);
- p_func_ptr_element->element->erase();
+void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement &p_func_ptr_element) {
+ ERR_FAIL_NULL(p_func_ptr_element.element);
+ ERR_FAIL_NULL(p_func_ptr_element.func_ptr);
+ MutexLock lock(p_func_ptr_element.func_ptr->mutex);
+ p_func_ptr_element.element->erase();
}
void GDScript::_fixup_thread_function_bookkeeping() {
// Transfer the ownership of these update items to the main thread,
// because the current one is dying, leaving theirs orphan, dangling.
- HashSet<GDScript *> scripts;
-
DEV_ASSERT(!Thread::is_main_thread());
- MutexLock lock(func_ptrs_to_update_main_thread->mutex);
- {
- MutexLock lock2(func_ptrs_to_update_thread_local.mutex);
-
- while (!func_ptrs_to_update_thread_local.ptrs.is_empty()) {
- // Transfer the thread-to-script records from the dying thread to the main one.
-
- List<GDScriptFunction **>::Element *E = func_ptrs_to_update_thread_local.ptrs.front();
- List<GDScriptFunction **>::Element *new_E = func_ptrs_to_update_main_thread->ptrs.push_front(E->get());
-
- GDScript *script = (*E->get())->get_script();
- if (!scripts.has(script)) {
- scripts.insert(script);
-
- // Replace dying thread by the main thread in the script-to-thread records.
-
- MutexLock lock3(script->func_ptrs_to_update_mutex);
- DEV_ASSERT(script->func_ptrs_to_update.find(&func_ptrs_to_update_thread_local));
- {
- for (List<UpdatableFuncPtrElement>::Element *F = script->func_ptrs_to_update_elems.front(); F; F = F->next()) {
- bool is_dying_thread_entry = F->get().mutex == &func_ptrs_to_update_thread_local.mutex;
- if (is_dying_thread_entry) {
- // This may lead to multiple main-thread entries, but that's not a problem
- // and allows to reuse the element, which is needed, since it's tracked by pointer.
- F->get().element = new_E;
- F->get().mutex = &func_ptrs_to_update_main_thread->mutex;
- }
- }
- }
- }
+ MutexLock lock(func_ptrs_to_update_main_thread.mutex);
+ MutexLock lock2(func_ptrs_to_update_thread_local->mutex);
- E->erase();
- }
- }
- func_ptrs_to_update_main_thread->initialized = true;
-
- {
- // Remove orphan thread-to-script entries from every script.
- // FIXME: This involves iterating through every script whenever a thread dies.
- // While it's OK that thread creation/destruction are heavy operations,
- // additional bookkeeping can be used to outperform this brute-force approach.
-
- GDScriptLanguage *gd_lang = GDScriptLanguage::get_singleton();
-
- MutexLock lock2(gd_lang->mutex);
-
- for (SelfList<GDScript> *s = gd_lang->script_list.first(); s; s = s->next()) {
- GDScript *script = s->self();
- for (List<UpdatableFuncPtr *>::Element *E = script->func_ptrs_to_update.front(); E; E = E->next()) {
- bool is_dying_thread_entry = &E->get()->mutex == &func_ptrs_to_update_thread_local.mutex;
- if (is_dying_thread_entry) {
- E->erase();
- break;
- }
- }
- }
+ while (!func_ptrs_to_update_thread_local->ptrs.is_empty()) {
+ List<GDScriptFunction **>::Element *E = func_ptrs_to_update_thread_local->ptrs.front();
+ E->transfer_to_back(&func_ptrs_to_update_main_thread.ptrs);
+ func_ptrs_to_update_thread_local->transferred = true;
}
}
@@ -1514,12 +1449,29 @@ void GDScript::clear(ClearData *p_clear_data) {
{
MutexLock outer_lock(func_ptrs_to_update_mutex);
for (UpdatableFuncPtr *updatable : func_ptrs_to_update) {
- MutexLock inner_lock(updatable->mutex);
- for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) {
- *func_ptr_ptr = nullptr;
+ bool destroy = false;
+ {
+ MutexLock inner_lock(updatable->mutex);
+ if (updatable->transferred) {
+ func_ptrs_to_update_main_thread.mutex.lock();
+ }
+ for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) {
+ *func_ptr_ptr = nullptr;
+ }
+ DEV_ASSERT(updatable->rc != 0);
+ updatable->rc--;
+ if (updatable->rc == 0) {
+ destroy = true;
+ }
+ if (updatable->transferred) {
+ func_ptrs_to_update_main_thread.mutex.unlock();
+ }
+ }
+ if (destroy) {
+ DEV_ASSERT(updatable != &func_ptrs_to_update_main_thread);
+ memdelete(updatable);
}
}
- func_ptrs_to_update_elems.clear();
}
RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
@@ -2139,8 +2091,31 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
named_globals.erase(p_name);
}
+void GDScriptLanguage::thread_enter() {
+ GDScript::func_ptrs_to_update_thread_local = memnew(GDScript::UpdatableFuncPtr);
+}
+
void GDScriptLanguage::thread_exit() {
+ // This thread may have been created before GDScript was up
+ // (which also means it can't have run any GDScript code at all).
+ if (!GDScript::func_ptrs_to_update_thread_local) {
+ return;
+ }
+
GDScript::_fixup_thread_function_bookkeeping();
+
+ bool destroy = false;
+ {
+ MutexLock lock(GDScript::func_ptrs_to_update_thread_local->mutex);
+ DEV_ASSERT(GDScript::func_ptrs_to_update_thread_local->rc != 0);
+ GDScript::func_ptrs_to_update_thread_local->rc--;
+ if (GDScript::func_ptrs_to_update_thread_local->rc == 0) {
+ destroy = true;
+ }
+ }
+ if (destroy) {
+ memdelete(GDScript::func_ptrs_to_update_thread_local);
+ }
}
void GDScriptLanguage::init() {
@@ -2175,6 +2150,8 @@ void GDScriptLanguage::init() {
_add_global(E.name, E.ptr);
}
+ GDScript::func_ptrs_to_update_thread_local = &GDScript::func_ptrs_to_update_main_thread;
+
#ifdef TESTS_ENABLED
GDScriptTests::GDScriptTestRunner::handle_cmdline();
#endif
@@ -2224,6 +2201,8 @@ void GDScriptLanguage::finish() {
}
script_list.clear();
function_list.clear();
+
+ DEV_ASSERT(GDScript::func_ptrs_to_update_main_thread.rc == 1);
}
void GDScriptLanguage::profiling_start() {
@@ -2241,6 +2220,8 @@ void GDScriptLanguage::profiling_start() {
elem->self()->profile.last_frame_call_count = 0;
elem->self()->profile.last_frame_self_time = 0;
elem->self()->profile.last_frame_total_time = 0;
+ elem->self()->profile.native_calls.clear();
+ elem->self()->profile.last_native_calls.clear();
elem = elem->next();
}
@@ -2248,6 +2229,13 @@ void GDScriptLanguage::profiling_start() {
#endif
}
+void GDScriptLanguage::profiling_set_save_native_calls(bool p_enable) {
+#ifdef DEBUG_ENABLED
+ MutexLock lock(mutex);
+ profile_native_calls = p_enable;
+#endif
+}
+
void GDScriptLanguage::profiling_stop() {
#ifdef DEBUG_ENABLED
MutexLock lock(this->mutex);
@@ -2262,17 +2250,32 @@ int GDScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr,
MutexLock lock(this->mutex);
+ profiling_collate_native_call_data(true);
SelfList<GDScriptFunction> *elem = function_list.first();
while (elem) {
if (current >= p_info_max) {
break;
}
+ int last_non_internal = current;
p_info_arr[current].call_count = elem->self()->profile.call_count.get();
p_info_arr[current].self_time = elem->self()->profile.self_time.get();
p_info_arr[current].total_time = elem->self()->profile.total_time.get();
p_info_arr[current].signature = elem->self()->profile.signature;
- elem = elem->next();
current++;
+
+ int nat_time = 0;
+ HashMap<String, GDScriptFunction::Profile::NativeProfile>::ConstIterator nat_calls = elem->self()->profile.native_calls.begin();
+ while (nat_calls) {
+ p_info_arr[current].call_count = nat_calls->value.call_count;
+ p_info_arr[current].total_time = nat_calls->value.total_time;
+ p_info_arr[current].self_time = nat_calls->value.total_time;
+ p_info_arr[current].signature = nat_calls->value.signature;
+ nat_time += nat_calls->value.total_time;
+ current++;
+ ++nat_calls;
+ }
+ p_info_arr[last_non_internal].internal_time = nat_time;
+ elem = elem->next();
}
#endif
@@ -2285,17 +2288,33 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_
#ifdef DEBUG_ENABLED
MutexLock lock(this->mutex);
+ profiling_collate_native_call_data(false);
SelfList<GDScriptFunction> *elem = function_list.first();
while (elem) {
if (current >= p_info_max) {
break;
}
if (elem->self()->profile.last_frame_call_count > 0) {
+ int last_non_internal = current;
p_info_arr[current].call_count = elem->self()->profile.last_frame_call_count;
p_info_arr[current].self_time = elem->self()->profile.last_frame_self_time;
p_info_arr[current].total_time = elem->self()->profile.last_frame_total_time;
p_info_arr[current].signature = elem->self()->profile.signature;
current++;
+
+ int nat_time = 0;
+ HashMap<String, GDScriptFunction::Profile::NativeProfile>::ConstIterator nat_calls = elem->self()->profile.last_native_calls.begin();
+ while (nat_calls) {
+ p_info_arr[current].call_count = nat_calls->value.call_count;
+ p_info_arr[current].total_time = nat_calls->value.total_time;
+ p_info_arr[current].self_time = nat_calls->value.total_time;
+ p_info_arr[current].internal_time = nat_calls->value.total_time;
+ p_info_arr[current].signature = nat_calls->value.signature;
+ nat_time += nat_calls->value.total_time;
+ current++;
+ ++nat_calls;
+ }
+ p_info_arr[last_non_internal].internal_time = nat_time;
}
elem = elem->next();
}
@@ -2304,6 +2323,33 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_
return current;
}
+void GDScriptLanguage::profiling_collate_native_call_data(bool p_accumulated) {
+#ifdef DEBUG_ENABLED
+ // The same native call can be called from multiple functions, so join them together here.
+ // Only use the name of the function (ie signature.split[2]).
+ HashMap<String, GDScriptFunction::Profile::NativeProfile *> seen_nat_calls;
+ SelfList<GDScriptFunction> *elem = function_list.first();
+ while (elem) {
+ HashMap<String, GDScriptFunction::Profile::NativeProfile> *nat_calls = p_accumulated ? &elem->self()->profile.native_calls : &elem->self()->profile.last_native_calls;
+ HashMap<String, GDScriptFunction::Profile::NativeProfile>::Iterator it = nat_calls->begin();
+
+ while (it != nat_calls->end()) {
+ Vector<String> sig = it->value.signature.split("::");
+ HashMap<String, GDScriptFunction::Profile::NativeProfile *>::ConstIterator already_found = seen_nat_calls.find(sig[2]);
+ if (already_found) {
+ already_found->value->total_time += it->value.total_time;
+ already_found->value->call_count += it->value.call_count;
+ elem->self()->profile.last_native_calls.remove(it);
+ } else {
+ seen_nat_calls.insert(sig[2], &it->value);
+ }
+ ++it;
+ }
+ elem = elem->next();
+ }
+#endif
+}
+
struct GDScriptDepSort {
//must support sorting so inheritance works properly (parent must be reloaded first)
bool operator()(const Ref<GDScript> &A, const Ref<GDScript> &B) const {
@@ -2340,6 +2386,19 @@ void GDScriptLanguage::reload_all_scripts() {
}
elem = elem->next();
}
+
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ // Reload all pointers to existing singletons so that tool scripts can work with the reloaded extensions.
+ List<Engine::Singleton> singletons;
+ Engine::get_singleton()->get_singletons(&singletons);
+ for (const Engine::Singleton &E : singletons) {
+ if (globals.has(E.name)) {
+ _add_global(E.name, E.ptr);
+ }
+ }
+ }
+#endif
}
//as scripts are going to be reloaded, must proceed without locking here
@@ -2490,9 +2549,11 @@ void GDScriptLanguage::frame() {
elem->self()->profile.last_frame_call_count = elem->self()->profile.frame_call_count.get();
elem->self()->profile.last_frame_self_time = elem->self()->profile.frame_self_time.get();
elem->self()->profile.last_frame_total_time = elem->self()->profile.frame_total_time.get();
+ elem->self()->profile.last_native_calls = elem->self()->profile.native_calls;
elem->self()->profile.frame_call_count.set(0);
elem->self()->profile.frame_self_time.set(0);
elem->self()->profile.frame_total_time.set(0);
+ elem->self()->profile.native_calls.clear();
elem = elem->next();
}
}