diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/gdscript/gdscript.cpp | 109 | ||||
-rw-r--r-- | modules/gdscript/gdscript.h | 14 | ||||
-rw-r--r-- | modules/gdscript/gdscript_lambda_callable.cpp | 4 | ||||
-rw-r--r-- | modules/gdscript/gdscript_lambda_callable.h | 4 | ||||
-rw-r--r-- | modules/jpg/image_loader_jpegd.cpp | 6 | ||||
-rw-r--r-- | modules/openxr/openxr_api.cpp | 2 | ||||
-rw-r--r-- | modules/text_server_adv/gdextension_build/methods.py | 4 | ||||
-rw-r--r-- | modules/text_server_fb/gdextension_build/methods.py | 4 | ||||
-rw-r--r-- | modules/webp/webp_common.cpp | 4 |
9 files changed, 127 insertions, 24 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 6245cc85a0..4accdb4d21 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -992,6 +992,7 @@ void GDScript::set_path(const String &p_path, bool p_take_over) { String old_path = path; path = p_path; + path_valid = true; GDScriptCache::move_script(old_path, p_path); for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) { @@ -1000,6 +1001,9 @@ void GDScript::set_path(const String &p_path, bool p_take_over) { } String GDScript::get_script_path() const { + if (!path_valid && !get_path().is_empty()) { + return get_path(); + } return path; } @@ -1035,6 +1039,7 @@ Error GDScript::load_source_code(const String &p_path) { source = s; path = p_path; + path_valid = true; #ifdef TOOLS_ENABLED source_changed_cache = true; set_edited(false); @@ -1387,33 +1392,106 @@ 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::UpdatableFuncPtrElement *GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) { + MutexLock lock(func_ptrs_to_update_mutex); -GDScript::UpdatableFuncPtrElement GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) { - UpdatableFuncPtrElement result = {}; + List<UpdatableFuncPtrElement>::Element *result = func_ptrs_to_update_elems.push_back(UpdatableFuncPtrElement()); { - 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.mutex = &func_ptrs_to_update_thread_local.mutex; + 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; if (likely(func_ptrs_to_update_thread_local.initialized)) { - return result; + return &result->get(); } func_ptrs_to_update_thread_local.initialized = true; } - MutexLock lock(func_ptrs_to_update_mutex); func_ptrs_to_update.push_back(&func_ptrs_to_update_thread_local); - return result; + return &result->get(); +} + +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.mutex); - MutexLock lock(*p_func_ptr_element.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; + } + } + } + } + + 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; + } + } + } + } } void GDScript::clear(ClearData *p_clear_data) { @@ -1441,6 +1519,7 @@ void GDScript::clear(ClearData *p_clear_data) { *func_ptr_ptr = nullptr; } } + func_ptrs_to_update_elems.clear(); } RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies(); @@ -2060,6 +2139,10 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) { named_globals.erase(p_name); } +void GDScriptLanguage::thread_exit() { + GDScript::_fixup_thread_function_bookkeeping(); +} + void GDScriptLanguage::init() { //populate global constants int gcc = CoreConstants::get_global_constant_count(); diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 04b0a1d786..9b99f5ca0b 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -128,11 +128,16 @@ class GDScript : public Script { Mutex *mutex = nullptr; }; static thread_local UpdatableFuncPtr func_ptrs_to_update_thread_local; + static thread_local LocalVector<List<UpdatableFuncPtr *>::Element> func_ptrs_to_update_entries_thread_local; + static UpdatableFuncPtr *func_ptrs_to_update_main_thread; List<UpdatableFuncPtr *> func_ptrs_to_update; + List<UpdatableFuncPtrElement> func_ptrs_to_update_elems; Mutex func_ptrs_to_update_mutex; - UpdatableFuncPtrElement _add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr); - static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement p_func_ptr_element); + UpdatableFuncPtrElement *_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr); + static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement *p_func_ptr_element); + + static void _fixup_thread_function_bookkeeping(); #ifdef TOOLS_ENABLED // For static data storage during hot-reloading. @@ -171,6 +176,7 @@ class GDScript : public Script { //exported members String source; String path; + bool path_valid = false; // False if using default path. StringName local_name; // Inner class identifier or `class_name`. StringName global_name; // `class_name`. String fully_qualified_name; @@ -553,6 +559,10 @@ public: virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value) override; virtual void remove_named_global_constant(const StringName &p_name) override; + /* MULTITHREAD FUNCTIONS */ + + virtual void thread_exit() override; + /* DEBUGGER FUNCTIONS */ virtual String debug_get_error() const override; diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp index 547f5607d3..339d1ac08e 100644 --- a/modules/gdscript/gdscript_lambda_callable.cpp +++ b/modules/gdscript/gdscript_lambda_callable.cpp @@ -296,5 +296,7 @@ GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptF } GDScriptLambdaSelfCallable::~GDScriptLambdaSelfCallable() { - GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element); + if (updatable_func_ptr_element) { + GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element); + } } diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h index ee7d547544..d961f18852 100644 --- a/modules/gdscript/gdscript_lambda_callable.h +++ b/modules/gdscript/gdscript_lambda_callable.h @@ -45,7 +45,7 @@ class GDScriptLambdaCallable : public CallableCustom { GDScriptFunction *function = nullptr; Ref<GDScript> script; uint32_t h; - GDScript::UpdatableFuncPtrElement updatable_func_ptr_element; + GDScript::UpdatableFuncPtrElement *updatable_func_ptr_element = nullptr; Vector<Variant> captures; @@ -72,7 +72,7 @@ class GDScriptLambdaSelfCallable : public CallableCustom { Ref<RefCounted> reference; // For objects that are RefCounted, keep a reference. Object *object = nullptr; // For non RefCounted objects, use a direct pointer. uint32_t h; - GDScript::UpdatableFuncPtrElement updatable_func_ptr_element; + GDScript::UpdatableFuncPtrElement *updatable_func_ptr_element = nullptr; Vector<Variant> captures; diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp index 0b9fcf4455..e7fa909706 100644 --- a/modules/jpg/image_loader_jpegd.cpp +++ b/modules/jpg/image_loader_jpegd.cpp @@ -156,7 +156,11 @@ public: static Error _jpgd_save_to_output_stream(jpge::output_stream *p_output_stream, const Ref<Image> &p_img, float p_quality) { ERR_FAIL_COND_V(p_img.is_null() || p_img->is_empty(), ERR_INVALID_PARAMETER); - Ref<Image> image = p_img; + Ref<Image> image = p_img->duplicate(); + if (image->is_compressed()) { + Error error = image->decompress(); + ERR_FAIL_COND_V_MSG(error != OK, error, "Couldn't decompress image."); + } if (image->get_format() != Image::FORMAT_RGB8) { image->convert(Image::FORMAT_RGB8); } diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index dc3ccccd08..3c606de670 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -289,7 +289,7 @@ bool OpenXRAPI::create_instance() { for (KeyValue<String, bool *> &requested_extension : requested_extensions) { if (!is_extension_supported(requested_extension.key)) { if (requested_extension.value == nullptr) { - // Null means this is a manditory extension so we fail. + // Null means this is a mandatory extension so we fail. ERR_FAIL_V_MSG(false, String("OpenXR: OpenXR Runtime does not support ") + requested_extension.key + String(" extension!")); } else { // Set this extension as not supported. diff --git a/modules/text_server_adv/gdextension_build/methods.py b/modules/text_server_adv/gdextension_build/methods.py index 3c5229462c..e58bc3abec 100644 --- a/modules/text_server_adv/gdextension_build/methods.py +++ b/modules/text_server_adv/gdextension_build/methods.py @@ -99,8 +99,8 @@ def make_icu_data(target, source, env): def write_macos_plist(target, binary_name, identifier, name): - os.makedirs(f"{target}/Resourece/", exist_ok=True) - f = open(f"{target}/Resourece/Info.plist", "w") + os.makedirs(f"{target}/Resource/", exist_ok=True) + f = open(f"{target}/Resource/Info.plist", "w") f.write(f'<?xml version="1.0" encoding="UTF-8"?>\n') f.write(f'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n') diff --git a/modules/text_server_fb/gdextension_build/methods.py b/modules/text_server_fb/gdextension_build/methods.py index 3c5229462c..e58bc3abec 100644 --- a/modules/text_server_fb/gdextension_build/methods.py +++ b/modules/text_server_fb/gdextension_build/methods.py @@ -99,8 +99,8 @@ def make_icu_data(target, source, env): def write_macos_plist(target, binary_name, identifier, name): - os.makedirs(f"{target}/Resourece/", exist_ok=True) - f = open(f"{target}/Resourece/Info.plist", "w") + os.makedirs(f"{target}/Resource/", exist_ok=True) + f = open(f"{target}/Resource/Info.plist", "w") f.write(f'<?xml version="1.0" encoding="UTF-8"?>\n') f.write(f'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n') diff --git a/modules/webp/webp_common.cpp b/modules/webp/webp_common.cpp index bc34a25733..3a2ac5a90e 100644 --- a/modules/webp/webp_common.cpp +++ b/modules/webp/webp_common.cpp @@ -59,6 +59,10 @@ Vector<uint8_t> _webp_packer(const Ref<Image> &p_image, float p_quality, bool p_ compression_method = CLAMP(compression_method, 0, 6); Ref<Image> img = p_image->duplicate(); + if (img->is_compressed()) { + Error error = img->decompress(); + ERR_FAIL_COND_V_MSG(error != OK, Vector<uint8_t>(), "Couldn't decompress image."); + } if (img->detect_alpha()) { img->convert(Image::FORMAT_RGBA8); } else { |