diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml index d314991feee..9524b5260b2 100644 --- a/.github/workflows/web_builds.yml +++ b/.github/workflows/web_builds.yml @@ -17,7 +17,24 @@ concurrency: jobs: web-template: runs-on: "ubuntu-22.04" - name: Template (target=template_release) + name: ${{ matrix.name }} + strategy: + fail-fast: false + matrix: + include: + - name: Template w/ threads (target=template_release, threads=yes) + cache-name: web-template + target: template_release + sconsflags: threads=yes + tests: false + artifact: true + + - name: Template w/o threads (target=template_release, threads=no) + cache-name: web-nothreads-template + target: template_release + sconsflags: threads=no + tests: false + artifact: true steps: - uses: actions/checkout@v4 @@ -34,6 +51,8 @@ jobs: - name: Setup Godot build cache uses: ./.github/actions/godot-cache + with: + cache-name: ${{ matrix.cache-name }} continue-on-error: true - name: Setup python and scons @@ -42,10 +61,13 @@ jobs: - name: Compilation uses: ./.github/actions/godot-build with: - sconsflags: ${{ env.SCONSFLAGS }} + sconsflags: ${{ env.SCONSFLAGS }} ${{ matrix.sconsflags }} platform: web - target: template_release - tests: false + target: ${{ matrix.target }} + tests: ${{ matrix.tests }} - name: Upload artifact uses: ./.github/actions/upload-artifact + if: ${{ matrix.artifact }} + with: + name: ${{ matrix.cache-name }} diff --git a/SConstruct b/SConstruct index c7b9d5bc865..6a4dea2c092 100644 --- a/SConstruct +++ b/SConstruct @@ -183,6 +183,7 @@ opts.Add(BoolVariable("separate_debug_symbols", "Extract debugging symbols to a opts.Add(EnumVariable("lto", "Link-time optimization (production builds)", "none", ("none", "auto", "thin", "full"))) opts.Add(BoolVariable("production", "Set defaults to build Godot for use in production", False)) opts.Add(BoolVariable("generate_apk", "Generate an APK/AAB after building Android library by calling Gradle", False)) +opts.Add(BoolVariable("threads", "Enable threading support", True)) # Components opts.Add(BoolVariable("deprecated", "Enable compatibility code for deprecated and removed features", True)) @@ -832,6 +833,10 @@ if selected_platform in platform_list: suffix += ".double" suffix += "." + env["arch"] + + if not env["threads"]: + suffix += ".nothreads" + suffix += env.extra_suffix sys.path.remove(tmppath) @@ -972,6 +977,9 @@ if selected_platform in platform_list: env.Tool("compilation_db") env.Alias("compiledb", env.CompilationDatabase()) + if env["threads"]: + env.Append(CPPDEFINES=["THREADS_ENABLED"]) + Export("env") # Build subdirs, the build order is dependent on link order. diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp index 8e8a2ef06b9..e2ab473b01d 100644 --- a/core/object/worker_thread_pool.cpp +++ b/core/object/worker_thread_pool.cpp @@ -47,6 +47,7 @@ WorkerThreadPool *WorkerThreadPool::singleton = nullptr; thread_local CommandQueueMT *WorkerThreadPool::flushing_cmd_queue = nullptr; void WorkerThreadPool::_process_task(Task *p_task) { +#ifdef THREADS_ENABLED int pool_thread_index = thread_ids[Thread::get_caller_id()]; ThreadData &curr_thread = threads[pool_thread_index]; Task *prev_task = nullptr; // In case this is recursively called. @@ -69,6 +70,7 @@ void WorkerThreadPool::_process_task(Task *p_task) { curr_thread.current_task = p_task; task_mutex.unlock(); } +#endif if (p_task->group) { // Handling a group @@ -143,6 +145,7 @@ void WorkerThreadPool::_process_task(Task *p_task) { } } +#ifdef THREADS_ENABLED { curr_thread.current_task = prev_task; if (p_task->low_priority) { @@ -159,6 +162,7 @@ void WorkerThreadPool::_process_task(Task *p_task) { } set_current_thread_safe_for_nodes(safe_for_nodes_backup); +#endif } void WorkerThreadPool::_thread_function(void *p_user) { @@ -542,6 +546,7 @@ bool WorkerThreadPool::is_group_task_completed(GroupID p_group) const { } void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) { +#ifdef THREADS_ENABLED task_mutex.lock(); Group **groupp = groups.getptr(p_group); task_mutex.unlock(); @@ -574,6 +579,7 @@ void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) { task_mutex.lock(); // This mutex is needed when Physics 2D and/or 3D is selected to run on a separate thread. groups.erase(p_group); task_mutex.unlock(); +#endif } int WorkerThreadPool::get_thread_index() { diff --git a/core/os/condition_variable.h b/core/os/condition_variable.h index 6a49ced31b4..2b6b272e18d 100644 --- a/core/os/condition_variable.h +++ b/core/os/condition_variable.h @@ -33,6 +33,8 @@ #include "core/os/mutex.h" +#ifdef THREADS_ENABLED + #ifdef MINGW_ENABLED #define MINGW_STDTHREAD_REDUNDANCY_WARNING #include "thirdparty/mingw-std-threads/mingw.condition_variable.h" @@ -66,4 +68,16 @@ public: } }; +#else // No threads. + +class ConditionVariable { +public: + template + void wait(const MutexLock &p_lock) const {} + void notify_one() const {} + void notify_all() const {} +}; + +#endif // THREADS_ENABLED + #endif // CONDITION_VARIABLE_H diff --git a/core/os/mutex.cpp b/core/os/mutex.cpp index 5d4e457c5fa..9a8a2a29618 100644 --- a/core/os/mutex.cpp +++ b/core/os/mutex.cpp @@ -40,7 +40,11 @@ void _global_unlock() { _global_mutex.unlock(); } +#ifdef THREADS_ENABLED + template class MutexImpl; template class MutexImpl; template class MutexLock>; template class MutexLock>; + +#endif diff --git a/core/os/mutex.h b/core/os/mutex.h index 03af48ca7c2..69f494d9cdb 100644 --- a/core/os/mutex.h +++ b/core/os/mutex.h @@ -43,6 +43,8 @@ #define THREADING_NAMESPACE std #endif +#ifdef THREADS_ENABLED + template class MutexLock; @@ -125,8 +127,8 @@ class MutexLock { THREADING_NAMESPACE::unique_lock lock; public: - _ALWAYS_INLINE_ explicit MutexLock(const MutexT &p_mutex) : - lock(p_mutex.mutex){}; + explicit MutexLock(const MutexT &p_mutex) : + lock(p_mutex.mutex) {} }; // This specialization is needed so manual locking and MutexLock can be used @@ -155,4 +157,38 @@ extern template class MutexImpl; extern template class MutexLock>; extern template class MutexLock>; +#else // No threads. + +class MutexImpl { + mutable THREADING_NAMESPACE::mutex mutex; + +public: + void lock() const {} + void unlock() const {} + bool try_lock() const { return true; } +}; + +template +class SafeBinaryMutex : public MutexImpl { + static thread_local uint32_t count; +}; + +template +class MutexLock { +public: + MutexLock(const MutexT &p_mutex) {} +}; + +template +class MutexLock> { +public: + MutexLock(const SafeBinaryMutex &p_mutex) {} + ~MutexLock() {} +}; + +using Mutex = MutexImpl; +using BinaryMutex = MutexImpl; + +#endif // THREADS_ENABLED + #endif // MUTEX_H diff --git a/core/os/os.cpp b/core/os/os.cpp index 26ae286979c..d5d9988cc1b 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -504,6 +504,12 @@ bool OS::has_feature(const String &p_feature) { } #endif +#ifdef THREADS_ENABLED + if (p_feature == "threads") { + return true; + } +#endif + if (_check_internal_feature_support(p_feature)) { return true; } diff --git a/core/os/semaphore.h b/core/os/semaphore.h index b8ae35b86b9..19ef1dedc0c 100644 --- a/core/os/semaphore.h +++ b/core/os/semaphore.h @@ -31,6 +31,10 @@ #ifndef SEMAPHORE_H #define SEMAPHORE_H +#include + +#ifdef THREADS_ENABLED + #include "core/error/error_list.h" #include "core/typedefs.h" #ifdef DEBUG_ENABLED @@ -132,4 +136,17 @@ public: #endif }; +#else // No threads. + +class Semaphore { +public: + void post(uint32_t p_count = 1) const {} + void wait() const {} + bool try_wait() const { + return true; + } +}; + +#endif // THREADS_ENABLED + #endif // SEMAPHORE_H diff --git a/core/os/thread.cpp b/core/os/thread.cpp index 2ba90ba42cd..afc74364f65 100644 --- a/core/os/thread.cpp +++ b/core/os/thread.cpp @@ -33,19 +33,22 @@ #include "thread.h" +#ifdef THREADS_ENABLED #include "core/object/script_language.h" #include "core/templates/safe_refcount.h" -Thread::PlatformFunctions Thread::platform_functions; - SafeNumeric Thread::id_counter(1); // The first value after .increment() is 2, hence by default the main thread ID should be 1. thread_local Thread::ID Thread::caller_id = Thread::UNASSIGNED_ID; +#endif + +Thread::PlatformFunctions Thread::platform_functions; void Thread::_set_platform_functions(const PlatformFunctions &p_functions) { platform_functions = p_functions; } +#ifdef THREADS_ENABLED void Thread::callback(ID p_caller_id, const Settings &p_settings, Callback p_callback, void *p_userdata) { Thread::caller_id = p_caller_id; if (platform_functions.set_priority) { @@ -107,4 +110,6 @@ Thread::~Thread() { } } +#endif // THREADS_ENABLED + #endif // PLATFORM_THREAD_OVERRIDE diff --git a/core/os/thread.h b/core/os/thread.h index cc954678f92..a0ecc24c916 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -53,6 +53,8 @@ class String; +#ifdef THREADS_ENABLED + class Thread { public: typedef void (*Callback)(void *p_userdata); @@ -86,6 +88,8 @@ public: private: friend class Main; + static PlatformFunctions platform_functions; + ID id = UNASSIGNED_ID; static SafeNumeric id_counter; static thread_local ID caller_id; @@ -93,8 +97,6 @@ private: static void callback(ID p_caller_id, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata); - static PlatformFunctions platform_functions; - static void make_main_thread() { caller_id = MAIN_ID; } static void release_main_thread() { caller_id = UNASSIGNED_ID; } @@ -125,6 +127,64 @@ public: ~Thread(); }; +#else // No threads. + +class Thread { +public: + typedef void (*Callback)(void *p_userdata); + + typedef uint64_t ID; + + enum : ID { + UNASSIGNED_ID = 0, + MAIN_ID = 1 + }; + + enum Priority { + PRIORITY_LOW, + PRIORITY_NORMAL, + PRIORITY_HIGH + }; + + struct Settings { + Priority priority; + Settings() { priority = PRIORITY_NORMAL; } + }; + + struct PlatformFunctions { + Error (*set_name)(const String &) = nullptr; + void (*set_priority)(Thread::Priority) = nullptr; + void (*init)() = nullptr; + void (*wrapper)(Thread::Callback, void *) = nullptr; + void (*term)() = nullptr; + }; + +private: + friend class Main; + + static PlatformFunctions platform_functions; + + static void make_main_thread() {} + static void release_main_thread() {} + +public: + static void _set_platform_functions(const PlatformFunctions &p_functions); + + _FORCE_INLINE_ ID get_id() const { return 0; } + _FORCE_INLINE_ static ID get_caller_id() { return MAIN_ID; } + _FORCE_INLINE_ static ID get_main_id() { return MAIN_ID; } + + _FORCE_INLINE_ static bool is_main_thread() { return true; } + + static Error set_name(const String &p_name) { return ERR_UNAVAILABLE; } + + void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings()) {} + bool is_started() const { return false; } + void wait_to_finish() {} +}; + +#endif // THREADS_ENABLED + #endif // THREAD_H #endif // PLATFORM_THREAD_OVERRIDE diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 51ea9234d4f..8126f74332e 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -153,7 +153,9 @@ int OS_Unix::unix_initialize_audio(int p_audio_driver) { } void OS_Unix::initialize_core() { +#ifdef THREADS_ENABLED init_thread_posix(); +#endif FileAccess::make_default(FileAccess::ACCESS_RESOURCES); FileAccess::make_default(FileAccess::ACCESS_USERDATA); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index aab433ac276..9fbe7ba6558 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -2354,7 +2354,11 @@ void EditorFileSystem::reimport_files(const Vector &p_files) { reimport_files.sort(); +#ifdef THREADS_ENABLED bool use_multiple_threads = GLOBAL_GET("editor/import/use_multiple_threads"); +#else + bool use_multiple_threads = false; +#endif int from = 0; for (int i = 0; i < reimport_files.size(); i++) { @@ -2680,6 +2684,10 @@ void EditorFileSystem::remove_import_format_support_query(Refinit(-1, 0.75); - } else { - int worker_threads = GLOBAL_GET("threading/worker_pool/max_threads"); - float low_priority_ratio = GLOBAL_GET("threading/worker_pool/low_priority_thread_ratio"); - WorkerThreadPool::get_singleton()->init(worker_threads, low_priority_ratio); + { +#ifdef THREADS_ENABLED + if (editor || project_manager) { + WorkerThreadPool::get_singleton()->init(-1, 0.75); + } else { + int worker_threads = GLOBAL_GET("threading/worker_pool/max_threads"); + float low_priority_ratio = GLOBAL_GET("threading/worker_pool/low_priority_thread_ratio"); + WorkerThreadPool::get_singleton()->init(worker_threads, low_priority_ratio); + } +#else + WorkerThreadPool::get_singleton()->init(0, 0); +#endif } #ifdef TOOLS_ENABLED diff --git a/misc/dist/html/editor.html b/misc/dist/html/editor.html index 93afbf085d6..e5c68c63384 100644 --- a/misc/dist/html/editor.html +++ b/misc/dist/html/editor.html @@ -23,7 +23,7 @@ - Godot Engine Web Editor (@GODOT_VERSION@) + Godot Engine Web Editor (___GODOT_VERSION___)