Add THREADS_ENABLED macro in order to compile Godot to run on the main thread
This commit is contained in:
parent
107f2961cc
commit
bd70b8e1f6
|
@ -17,7 +17,24 @@ concurrency:
|
||||||
jobs:
|
jobs:
|
||||||
web-template:
|
web-template:
|
||||||
runs-on: "ubuntu-22.04"
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
@ -34,6 +51,8 @@ jobs:
|
||||||
|
|
||||||
- name: Setup Godot build cache
|
- name: Setup Godot build cache
|
||||||
uses: ./.github/actions/godot-cache
|
uses: ./.github/actions/godot-cache
|
||||||
|
with:
|
||||||
|
cache-name: ${{ matrix.cache-name }}
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Setup python and scons
|
- name: Setup python and scons
|
||||||
|
@ -42,10 +61,13 @@ jobs:
|
||||||
- name: Compilation
|
- name: Compilation
|
||||||
uses: ./.github/actions/godot-build
|
uses: ./.github/actions/godot-build
|
||||||
with:
|
with:
|
||||||
sconsflags: ${{ env.SCONSFLAGS }}
|
sconsflags: ${{ env.SCONSFLAGS }} ${{ matrix.sconsflags }}
|
||||||
platform: web
|
platform: web
|
||||||
target: template_release
|
target: ${{ matrix.target }}
|
||||||
tests: false
|
tests: ${{ matrix.tests }}
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: ./.github/actions/upload-artifact
|
uses: ./.github/actions/upload-artifact
|
||||||
|
if: ${{ matrix.artifact }}
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.cache-name }}
|
||||||
|
|
|
@ -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(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("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("generate_apk", "Generate an APK/AAB after building Android library by calling Gradle", False))
|
||||||
|
opts.Add(BoolVariable("threads", "Enable threading support", True))
|
||||||
|
|
||||||
# Components
|
# Components
|
||||||
opts.Add(BoolVariable("deprecated", "Enable compatibility code for deprecated and removed features", True))
|
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 += ".double"
|
||||||
|
|
||||||
suffix += "." + env["arch"]
|
suffix += "." + env["arch"]
|
||||||
|
|
||||||
|
if not env["threads"]:
|
||||||
|
suffix += ".nothreads"
|
||||||
|
|
||||||
suffix += env.extra_suffix
|
suffix += env.extra_suffix
|
||||||
|
|
||||||
sys.path.remove(tmppath)
|
sys.path.remove(tmppath)
|
||||||
|
@ -972,6 +977,9 @@ if selected_platform in platform_list:
|
||||||
env.Tool("compilation_db")
|
env.Tool("compilation_db")
|
||||||
env.Alias("compiledb", env.CompilationDatabase())
|
env.Alias("compiledb", env.CompilationDatabase())
|
||||||
|
|
||||||
|
if env["threads"]:
|
||||||
|
env.Append(CPPDEFINES=["THREADS_ENABLED"])
|
||||||
|
|
||||||
Export("env")
|
Export("env")
|
||||||
|
|
||||||
# Build subdirs, the build order is dependent on link order.
|
# Build subdirs, the build order is dependent on link order.
|
||||||
|
|
|
@ -47,6 +47,7 @@ WorkerThreadPool *WorkerThreadPool::singleton = nullptr;
|
||||||
thread_local CommandQueueMT *WorkerThreadPool::flushing_cmd_queue = nullptr;
|
thread_local CommandQueueMT *WorkerThreadPool::flushing_cmd_queue = nullptr;
|
||||||
|
|
||||||
void WorkerThreadPool::_process_task(Task *p_task) {
|
void WorkerThreadPool::_process_task(Task *p_task) {
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
int pool_thread_index = thread_ids[Thread::get_caller_id()];
|
int pool_thread_index = thread_ids[Thread::get_caller_id()];
|
||||||
ThreadData &curr_thread = threads[pool_thread_index];
|
ThreadData &curr_thread = threads[pool_thread_index];
|
||||||
Task *prev_task = nullptr; // In case this is recursively called.
|
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;
|
curr_thread.current_task = p_task;
|
||||||
task_mutex.unlock();
|
task_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (p_task->group) {
|
if (p_task->group) {
|
||||||
// Handling a group
|
// Handling a group
|
||||||
|
@ -143,6 +145,7 @@ void WorkerThreadPool::_process_task(Task *p_task) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
{
|
{
|
||||||
curr_thread.current_task = prev_task;
|
curr_thread.current_task = prev_task;
|
||||||
if (p_task->low_priority) {
|
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);
|
set_current_thread_safe_for_nodes(safe_for_nodes_backup);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorkerThreadPool::_thread_function(void *p_user) {
|
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) {
|
void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) {
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
task_mutex.lock();
|
task_mutex.lock();
|
||||||
Group **groupp = groups.getptr(p_group);
|
Group **groupp = groups.getptr(p_group);
|
||||||
task_mutex.unlock();
|
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.
|
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);
|
groups.erase(p_group);
|
||||||
task_mutex.unlock();
|
task_mutex.unlock();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int WorkerThreadPool::get_thread_index() {
|
int WorkerThreadPool::get_thread_index() {
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
|
|
||||||
#include "core/os/mutex.h"
|
#include "core/os/mutex.h"
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
|
|
||||||
#ifdef MINGW_ENABLED
|
#ifdef MINGW_ENABLED
|
||||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||||
#include "thirdparty/mingw-std-threads/mingw.condition_variable.h"
|
#include "thirdparty/mingw-std-threads/mingw.condition_variable.h"
|
||||||
|
@ -66,4 +68,16 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#else // No threads.
|
||||||
|
|
||||||
|
class ConditionVariable {
|
||||||
|
public:
|
||||||
|
template <class BinaryMutexT>
|
||||||
|
void wait(const MutexLock<BinaryMutexT> &p_lock) const {}
|
||||||
|
void notify_one() const {}
|
||||||
|
void notify_all() const {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // THREADS_ENABLED
|
||||||
|
|
||||||
#endif // CONDITION_VARIABLE_H
|
#endif // CONDITION_VARIABLE_H
|
||||||
|
|
|
@ -40,7 +40,11 @@ void _global_unlock() {
|
||||||
_global_mutex.unlock();
|
_global_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
|
|
||||||
template class MutexImpl<THREADING_NAMESPACE::recursive_mutex>;
|
template class MutexImpl<THREADING_NAMESPACE::recursive_mutex>;
|
||||||
template class MutexImpl<THREADING_NAMESPACE::mutex>;
|
template class MutexImpl<THREADING_NAMESPACE::mutex>;
|
||||||
template class MutexLock<MutexImpl<THREADING_NAMESPACE::recursive_mutex>>;
|
template class MutexLock<MutexImpl<THREADING_NAMESPACE::recursive_mutex>>;
|
||||||
template class MutexLock<MutexImpl<THREADING_NAMESPACE::mutex>>;
|
template class MutexLock<MutexImpl<THREADING_NAMESPACE::mutex>>;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -43,6 +43,8 @@
|
||||||
#define THREADING_NAMESPACE std
|
#define THREADING_NAMESPACE std
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
|
|
||||||
template <class MutexT>
|
template <class MutexT>
|
||||||
class MutexLock;
|
class MutexLock;
|
||||||
|
|
||||||
|
@ -125,8 +127,8 @@ class MutexLock {
|
||||||
THREADING_NAMESPACE::unique_lock<typename MutexT::StdMutexType> lock;
|
THREADING_NAMESPACE::unique_lock<typename MutexT::StdMutexType> lock;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
_ALWAYS_INLINE_ explicit MutexLock(const MutexT &p_mutex) :
|
explicit MutexLock(const MutexT &p_mutex) :
|
||||||
lock(p_mutex.mutex){};
|
lock(p_mutex.mutex) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This specialization is needed so manual locking and MutexLock can be used
|
// This specialization is needed so manual locking and MutexLock can be used
|
||||||
|
@ -155,4 +157,38 @@ extern template class MutexImpl<THREADING_NAMESPACE::mutex>;
|
||||||
extern template class MutexLock<MutexImpl<THREADING_NAMESPACE::recursive_mutex>>;
|
extern template class MutexLock<MutexImpl<THREADING_NAMESPACE::recursive_mutex>>;
|
||||||
extern template class MutexLock<MutexImpl<THREADING_NAMESPACE::mutex>>;
|
extern template class MutexLock<MutexImpl<THREADING_NAMESPACE::mutex>>;
|
||||||
|
|
||||||
|
#else // No threads.
|
||||||
|
|
||||||
|
class MutexImpl {
|
||||||
|
mutable THREADING_NAMESPACE::mutex mutex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void lock() const {}
|
||||||
|
void unlock() const {}
|
||||||
|
bool try_lock() const { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int Tag>
|
||||||
|
class SafeBinaryMutex : public MutexImpl {
|
||||||
|
static thread_local uint32_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class MutexT>
|
||||||
|
class MutexLock {
|
||||||
|
public:
|
||||||
|
MutexLock(const MutexT &p_mutex) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int Tag>
|
||||||
|
class MutexLock<SafeBinaryMutex<Tag>> {
|
||||||
|
public:
|
||||||
|
MutexLock(const SafeBinaryMutex<Tag> &p_mutex) {}
|
||||||
|
~MutexLock() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
using Mutex = MutexImpl;
|
||||||
|
using BinaryMutex = MutexImpl;
|
||||||
|
|
||||||
|
#endif // THREADS_ENABLED
|
||||||
|
|
||||||
#endif // MUTEX_H
|
#endif // MUTEX_H
|
||||||
|
|
|
@ -504,6 +504,12 @@ bool OS::has_feature(const String &p_feature) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
|
if (p_feature == "threads") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (_check_internal_feature_support(p_feature)) {
|
if (_check_internal_feature_support(p_feature)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,10 @@
|
||||||
#ifndef SEMAPHORE_H
|
#ifndef SEMAPHORE_H
|
||||||
#define SEMAPHORE_H
|
#define SEMAPHORE_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
|
|
||||||
#include "core/error/error_list.h"
|
#include "core/error/error_list.h"
|
||||||
#include "core/typedefs.h"
|
#include "core/typedefs.h"
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
|
@ -132,4 +136,17 @@ public:
|
||||||
#endif
|
#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
|
#endif // SEMAPHORE_H
|
||||||
|
|
|
@ -33,19 +33,22 @@
|
||||||
|
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
#include "core/object/script_language.h"
|
#include "core/object/script_language.h"
|
||||||
#include "core/templates/safe_refcount.h"
|
#include "core/templates/safe_refcount.h"
|
||||||
|
|
||||||
Thread::PlatformFunctions Thread::platform_functions;
|
|
||||||
|
|
||||||
SafeNumeric<uint64_t> Thread::id_counter(1); // The first value after .increment() is 2, hence by default the main thread ID should be 1.
|
SafeNumeric<uint64_t> 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;
|
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) {
|
void Thread::_set_platform_functions(const PlatformFunctions &p_functions) {
|
||||||
platform_functions = 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) {
|
void Thread::callback(ID p_caller_id, const Settings &p_settings, Callback p_callback, void *p_userdata) {
|
||||||
Thread::caller_id = p_caller_id;
|
Thread::caller_id = p_caller_id;
|
||||||
if (platform_functions.set_priority) {
|
if (platform_functions.set_priority) {
|
||||||
|
@ -107,4 +110,6 @@ Thread::~Thread() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // THREADS_ENABLED
|
||||||
|
|
||||||
#endif // PLATFORM_THREAD_OVERRIDE
|
#endif // PLATFORM_THREAD_OVERRIDE
|
||||||
|
|
|
@ -53,6 +53,8 @@
|
||||||
|
|
||||||
class String;
|
class String;
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
|
|
||||||
class Thread {
|
class Thread {
|
||||||
public:
|
public:
|
||||||
typedef void (*Callback)(void *p_userdata);
|
typedef void (*Callback)(void *p_userdata);
|
||||||
|
@ -86,6 +88,8 @@ public:
|
||||||
private:
|
private:
|
||||||
friend class Main;
|
friend class Main;
|
||||||
|
|
||||||
|
static PlatformFunctions platform_functions;
|
||||||
|
|
||||||
ID id = UNASSIGNED_ID;
|
ID id = UNASSIGNED_ID;
|
||||||
static SafeNumeric<uint64_t> id_counter;
|
static SafeNumeric<uint64_t> id_counter;
|
||||||
static thread_local ID caller_id;
|
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 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 make_main_thread() { caller_id = MAIN_ID; }
|
||||||
static void release_main_thread() { caller_id = UNASSIGNED_ID; }
|
static void release_main_thread() { caller_id = UNASSIGNED_ID; }
|
||||||
|
|
||||||
|
@ -125,6 +127,64 @@ public:
|
||||||
~Thread();
|
~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 // THREAD_H
|
||||||
|
|
||||||
#endif // PLATFORM_THREAD_OVERRIDE
|
#endif // PLATFORM_THREAD_OVERRIDE
|
||||||
|
|
|
@ -153,7 +153,9 @@ int OS_Unix::unix_initialize_audio(int p_audio_driver) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS_Unix::initialize_core() {
|
void OS_Unix::initialize_core() {
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
init_thread_posix();
|
init_thread_posix();
|
||||||
|
#endif
|
||||||
|
|
||||||
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
|
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
|
||||||
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
|
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
|
||||||
|
|
|
@ -2354,7 +2354,11 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
|
||||||
|
|
||||||
reimport_files.sort();
|
reimport_files.sort();
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
bool use_multiple_threads = GLOBAL_GET("editor/import/use_multiple_threads");
|
bool use_multiple_threads = GLOBAL_GET("editor/import/use_multiple_threads");
|
||||||
|
#else
|
||||||
|
bool use_multiple_threads = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
int from = 0;
|
int from = 0;
|
||||||
for (int i = 0; i < reimport_files.size(); i++) {
|
for (int i = 0; i < reimport_files.size(); i++) {
|
||||||
|
@ -2680,6 +2684,10 @@ void EditorFileSystem::remove_import_format_support_query(Ref<EditorFileSystemIm
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorFileSystem::EditorFileSystem() {
|
EditorFileSystem::EditorFileSystem() {
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
|
use_threads = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
ResourceLoader::import = _resource_import;
|
ResourceLoader::import = _resource_import;
|
||||||
reimport_on_missing_imported_files = GLOBAL_GET("editor/import/reimport_missing_imported_files");
|
reimport_on_missing_imported_files = GLOBAL_GET("editor/import/reimport_missing_imported_files");
|
||||||
singleton = this;
|
singleton = this;
|
||||||
|
|
|
@ -165,7 +165,7 @@ class EditorFileSystem : public Node {
|
||||||
EditorFileSystemDirectory::FileInfo *new_file = nullptr;
|
EditorFileSystemDirectory::FileInfo *new_file = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool use_threads = true;
|
bool use_threads = false;
|
||||||
Thread thread;
|
Thread thread;
|
||||||
static void _thread_func(void *_userdata);
|
static void _thread_func(void *_userdata);
|
||||||
|
|
||||||
|
|
|
@ -1615,6 +1615,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize WorkerThreadPool.
|
// Initialize WorkerThreadPool.
|
||||||
|
{
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
if (editor || project_manager) {
|
if (editor || project_manager) {
|
||||||
WorkerThreadPool::get_singleton()->init(-1, 0.75);
|
WorkerThreadPool::get_singleton()->init(-1, 0.75);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1622,6 +1624,10 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||||
float low_priority_ratio = GLOBAL_GET("threading/worker_pool/low_priority_thread_ratio");
|
float low_priority_ratio = GLOBAL_GET("threading/worker_pool/low_priority_thread_ratio");
|
||||||
WorkerThreadPool::get_singleton()->init(worker_threads, low_priority_ratio);
|
WorkerThreadPool::get_singleton()->init(worker_threads, low_priority_ratio);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
WorkerThreadPool::get_singleton()->init(0, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
if (editor) {
|
if (editor) {
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
<link id="-gd-engine-icon" rel="icon" type="image/png" href="favicon.png">
|
<link id="-gd-engine-icon" rel="icon" type="image/png" href="favicon.png">
|
||||||
<link rel="apple-touch-icon" type="image/png" href="favicon.png">
|
<link rel="apple-touch-icon" type="image/png" href="favicon.png">
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
<title>Godot Engine Web Editor (@GODOT_VERSION@)</title>
|
<title>Godot Engine Web Editor (___GODOT_VERSION___)</title>
|
||||||
<style>
|
<style>
|
||||||
*:focus {
|
*:focus {
|
||||||
/* More visible outline for better keyboard navigation. */
|
/* More visible outline for better keyboard navigation. */
|
||||||
|
@ -294,7 +294,7 @@ a:active {
|
||||||
<br >
|
<br >
|
||||||
<img src="logo.svg" alt="Godot Engine logo" width="1024" height="414" style="width: auto; height: auto; max-width: min(85%, 50vh); max-height: 250px">
|
<img src="logo.svg" alt="Godot Engine logo" width="1024" height="414" style="width: auto; height: auto; max-width: min(85%, 50vh); max-height: 250px">
|
||||||
<br >
|
<br >
|
||||||
@GODOT_VERSION@
|
___GODOT_VERSION___
|
||||||
<br >
|
<br >
|
||||||
<a href="releases/">Need an old version?</a>
|
<a href="releases/">Need an old version?</a>
|
||||||
<br >
|
<br >
|
||||||
|
@ -384,7 +384,9 @@ window.addEventListener('load', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const missing = Engine.getMissingFeatures();
|
const missing = Engine.getMissingFeatures({
|
||||||
|
threads: ___GODOT_THREADS_ENABLED___,
|
||||||
|
});
|
||||||
if (missing.length) {
|
if (missing.length) {
|
||||||
// Display error dialog as threading support is required for the editor.
|
// Display error dialog as threading support is required for the editor.
|
||||||
document.getElementById('startButton').disabled = 'disabled';
|
document.getElementById('startButton').disabled = 'disabled';
|
||||||
|
|
|
@ -136,6 +136,7 @@ body {
|
||||||
<script src="$GODOT_URL"></script>
|
<script src="$GODOT_URL"></script>
|
||||||
<script>
|
<script>
|
||||||
const GODOT_CONFIG = $GODOT_CONFIG;
|
const GODOT_CONFIG = $GODOT_CONFIG;
|
||||||
|
const GODOT_THREADS_ENABLED = $GODOT_THREADS_ENABLED;
|
||||||
const engine = new Engine(GODOT_CONFIG);
|
const engine = new Engine(GODOT_CONFIG);
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
|
@ -213,7 +214,9 @@ const engine = new Engine(GODOT_CONFIG);
|
||||||
initializing = false;
|
initializing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const missing = Engine.getMissingFeatures();
|
const missing = Engine.getMissingFeatures({
|
||||||
|
threads: GODOT_THREADS_ENABLED,
|
||||||
|
});
|
||||||
if (missing.length !== 0) {
|
if (missing.length !== 0) {
|
||||||
const missingMsg = 'Error\nThe following features required to run Godot projects on the Web are missing:\n';
|
const missingMsg = 'Error\nThe following features required to run Godot projects on the Web are missing:\n';
|
||||||
displayFailureNotice(missingMsg + missing.join('\n'));
|
displayFailureNotice(missingMsg + missing.join('\n'));
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
// that they need an Internet connection to run the project if desired.
|
// that they need an Internet connection to run the project if desired.
|
||||||
// Incrementing CACHE_VERSION will kick off the install event and force
|
// Incrementing CACHE_VERSION will kick off the install event and force
|
||||||
// previously cached resources to be updated from the network.
|
// previously cached resources to be updated from the network.
|
||||||
const CACHE_VERSION = "@GODOT_VERSION@";
|
const CACHE_VERSION = "___GODOT_VERSION___";
|
||||||
const CACHE_PREFIX = "@GODOT_NAME@-sw-cache-";
|
const CACHE_PREFIX = "___GODOT_NAME___-sw-cache-";
|
||||||
const CACHE_NAME = CACHE_PREFIX + CACHE_VERSION;
|
const CACHE_NAME = CACHE_PREFIX + CACHE_VERSION;
|
||||||
const OFFLINE_URL = "@GODOT_OFFLINE_PAGE@";
|
const OFFLINE_URL = "___GODOT_OFFLINE_PAGE___";
|
||||||
// Files that will be cached on load.
|
// Files that will be cached on load.
|
||||||
const CACHED_FILES = @GODOT_CACHE@;
|
const CACHED_FILES = ___GODOT_CACHE___;
|
||||||
// Files that we might not want the user to preload, and will only be cached on first load.
|
// Files that we might not want the user to preload, and will only be cached on first load.
|
||||||
const CACHABLE_FILES = @GODOT_OPT_CACHE@;
|
const CACHABLE_FILES = ___GODOT_OPT_CACHE___;
|
||||||
const FULL_CACHE = CACHED_FILES.concat(CACHABLE_FILES);
|
const FULL_CACHE = CACHED_FILES.concat(CACHABLE_FILES);
|
||||||
|
|
||||||
self.addEventListener("install", (event) => {
|
self.addEventListener("install", (event) => {
|
||||||
|
@ -22,7 +22,7 @@ self.addEventListener("activate", (event) => {
|
||||||
function (keys) {
|
function (keys) {
|
||||||
// Remove old caches.
|
// Remove old caches.
|
||||||
return Promise.all(keys.filter(key => key.startsWith(CACHE_PREFIX) && key != CACHE_NAME).map(key => caches.delete(key)));
|
return Promise.all(keys.filter(key => key.startsWith(CACHE_PREFIX) && key != CACHE_NAME).map(key => caches.delete(key)));
|
||||||
}).then(function() {
|
}).then(function () {
|
||||||
// Enable navigation preload if available.
|
// Enable navigation preload if available.
|
||||||
return ("navigationPreload" in self.registration) ? self.registration.navigationPreload.enable() : Promise.resolve();
|
return ("navigationPreload" in self.registration) ? self.registration.navigationPreload.enable() : Promise.resolve();
|
||||||
})
|
})
|
||||||
|
|
|
@ -34,6 +34,12 @@
|
||||||
|
|
||||||
#include <thorvg.h>
|
#include <thorvg.h>
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
|
#define TVG_THREADS 1
|
||||||
|
#else
|
||||||
|
#define TVG_THREADS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
static Ref<ImageLoaderSVG> image_loader_svg;
|
static Ref<ImageLoaderSVG> image_loader_svg;
|
||||||
|
|
||||||
void initialize_svg_module(ModuleInitializationLevel p_level) {
|
void initialize_svg_module(ModuleInitializationLevel p_level) {
|
||||||
|
@ -42,7 +48,8 @@ void initialize_svg_module(ModuleInitializationLevel p_level) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw;
|
tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw;
|
||||||
if (tvg::Initializer::init(tvgEngine, 1) != tvg::Result::Success) {
|
|
||||||
|
if (tvg::Initializer::init(tvgEngine, TVG_THREADS) != tvg::Result::Success) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,5 +15,7 @@ module.exports = {
|
||||||
"Godot": true,
|
"Godot": true,
|
||||||
"Engine": true,
|
"Engine": true,
|
||||||
"$GODOT_CONFIG": true,
|
"$GODOT_CONFIG": true,
|
||||||
|
"$GODOT_THREADS_ENABLED": true,
|
||||||
|
"___GODOT_THREADS_ENABLED___": true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@ if "serve" in COMMAND_LINE_TARGETS or "run" in COMMAND_LINE_TARGETS:
|
||||||
except Exception:
|
except Exception:
|
||||||
print("GODOT_WEB_TEST_PORT must be a valid integer")
|
print("GODOT_WEB_TEST_PORT must be a valid integer")
|
||||||
sys.exit(255)
|
sys.exit(255)
|
||||||
serve(env.Dir("#bin/.web_zip").abspath, port, "run" in COMMAND_LINE_TARGETS)
|
serve(env.Dir(env.GetTemplateZipPath()).abspath, port, "run" in COMMAND_LINE_TARGETS)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
web_files = [
|
web_files = [
|
||||||
|
@ -95,7 +95,7 @@ engine = [
|
||||||
"js/engine/engine.js",
|
"js/engine/engine.js",
|
||||||
]
|
]
|
||||||
externs = [env.File("#platform/web/js/engine/engine.externs.js")]
|
externs = [env.File("#platform/web/js/engine/engine.externs.js")]
|
||||||
js_engine = env.CreateEngineFile("#bin/godot${PROGSUFFIX}.engine.js", engine, externs)
|
js_engine = env.CreateEngineFile("#bin/godot${PROGSUFFIX}.engine.js", engine, externs, env["threads"])
|
||||||
env.Depends(js_engine, externs)
|
env.Depends(js_engine, externs)
|
||||||
|
|
||||||
wrap_list = [
|
wrap_list = [
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
|
|
||||||
#include "audio_driver_web.h"
|
#include "audio_driver_web.h"
|
||||||
|
|
||||||
|
#include "godot_audio.h"
|
||||||
|
|
||||||
#include "core/config/project_settings.h"
|
#include "core/config/project_settings.h"
|
||||||
|
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
|
@ -184,6 +186,8 @@ Error AudioDriverWeb::input_stop() {
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
|
|
||||||
/// AudioWorkletNode implementation (threads)
|
/// AudioWorkletNode implementation (threads)
|
||||||
void AudioDriverWorklet::_audio_thread_func(void *p_data) {
|
void AudioDriverWorklet::_audio_thread_func(void *p_data) {
|
||||||
AudioDriverWorklet *driver = static_cast<AudioDriverWorklet *>(p_data);
|
AudioDriverWorklet *driver = static_cast<AudioDriverWorklet *>(p_data);
|
||||||
|
@ -245,3 +249,51 @@ void AudioDriverWorklet::finish_driver() {
|
||||||
quit = true; // Ask thread to quit.
|
quit = true; // Ask thread to quit.
|
||||||
thread.wait_to_finish();
|
thread.wait_to_finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else // No threads.
|
||||||
|
|
||||||
|
/// AudioWorkletNode implementation (no threads)
|
||||||
|
AudioDriverWorklet *AudioDriverWorklet::singleton = nullptr;
|
||||||
|
|
||||||
|
Error AudioDriverWorklet::create(int &p_buffer_size, int p_channels) {
|
||||||
|
if (!godot_audio_has_worklet()) {
|
||||||
|
return ERR_UNAVAILABLE;
|
||||||
|
}
|
||||||
|
return (Error)godot_audio_worklet_create(p_channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioDriverWorklet::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) {
|
||||||
|
_audio_driver_process();
|
||||||
|
godot_audio_worklet_start_no_threads(p_out_buf, p_out_buf_size, &_process_callback, p_in_buf, p_in_buf_size, &_capture_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioDriverWorklet::_process_callback(int p_pos, int p_samples) {
|
||||||
|
AudioDriverWorklet *driver = AudioDriverWorklet::get_singleton();
|
||||||
|
driver->_audio_driver_process(p_pos, p_samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioDriverWorklet::_capture_callback(int p_pos, int p_samples) {
|
||||||
|
AudioDriverWorklet *driver = AudioDriverWorklet::get_singleton();
|
||||||
|
driver->_audio_driver_capture(p_pos, p_samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ScriptProcessorNode implementation
|
||||||
|
AudioDriverScriptProcessor *AudioDriverScriptProcessor::singleton = nullptr;
|
||||||
|
|
||||||
|
void AudioDriverScriptProcessor::_process_callback() {
|
||||||
|
AudioDriverScriptProcessor::get_singleton()->_audio_driver_capture();
|
||||||
|
AudioDriverScriptProcessor::get_singleton()->_audio_driver_process();
|
||||||
|
}
|
||||||
|
|
||||||
|
Error AudioDriverScriptProcessor::create(int &p_buffer_samples, int p_channels) {
|
||||||
|
if (!godot_audio_has_script_processor()) {
|
||||||
|
return ERR_UNAVAILABLE;
|
||||||
|
}
|
||||||
|
return (Error)godot_audio_script_create(&p_buffer_samples, p_channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioDriverScriptProcessor::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) {
|
||||||
|
godot_audio_script_start(p_in_buf, p_in_buf_size, p_out_buf, p_out_buf_size, &_process_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // THREADS_ENABLED
|
||||||
|
|
|
@ -90,6 +90,7 @@ public:
|
||||||
AudioDriverWeb() {}
|
AudioDriverWeb() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
class AudioDriverWorklet : public AudioDriverWeb {
|
class AudioDriverWorklet : public AudioDriverWeb {
|
||||||
private:
|
private:
|
||||||
enum {
|
enum {
|
||||||
|
@ -120,4 +121,54 @@ public:
|
||||||
virtual void unlock() override;
|
virtual void unlock() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
class AudioDriverWorklet : public AudioDriverWeb {
|
||||||
|
private:
|
||||||
|
static void _process_callback(int p_pos, int p_samples);
|
||||||
|
static void _capture_callback(int p_pos, int p_samples);
|
||||||
|
|
||||||
|
static AudioDriverWorklet *singleton;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual Error create(int &p_buffer_size, int p_output_channels) override;
|
||||||
|
virtual void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual const char *get_name() const override {
|
||||||
|
return "AudioWorklet";
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void lock() override {}
|
||||||
|
virtual void unlock() override {}
|
||||||
|
|
||||||
|
static AudioDriverWorklet *get_singleton() { return singleton; }
|
||||||
|
|
||||||
|
AudioDriverWorklet() { singleton = this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioDriverScriptProcessor : public AudioDriverWeb {
|
||||||
|
private:
|
||||||
|
static void _process_callback();
|
||||||
|
|
||||||
|
static AudioDriverScriptProcessor *singleton;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual Error create(int &p_buffer_size, int p_output_channels) override;
|
||||||
|
virtual void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) override;
|
||||||
|
virtual void finish_driver() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual const char *get_name() const override { return "ScriptProcessor"; }
|
||||||
|
|
||||||
|
virtual void lock() override {}
|
||||||
|
virtual void unlock() override {}
|
||||||
|
|
||||||
|
static AudioDriverScriptProcessor *get_singleton() { return singleton; }
|
||||||
|
|
||||||
|
AudioDriverScriptProcessor() { singleton = this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // THREADS_ENABLED
|
||||||
|
|
||||||
#endif // AUDIO_DRIVER_WEB_H
|
#endif // AUDIO_DRIVER_WEB_H
|
||||||
|
|
|
@ -8,6 +8,7 @@ from emscripten_helpers import (
|
||||||
add_js_pre,
|
add_js_pre,
|
||||||
add_js_externs,
|
add_js_externs,
|
||||||
create_template_zip,
|
create_template_zip,
|
||||||
|
get_template_zip_path,
|
||||||
)
|
)
|
||||||
from methods import get_compiler_version
|
from methods import get_compiler_version
|
||||||
from SCons.Util import WhereIs
|
from SCons.Util import WhereIs
|
||||||
|
@ -161,6 +162,9 @@ def configure(env: "Environment"):
|
||||||
# Add method that joins/compiles our Engine files.
|
# Add method that joins/compiles our Engine files.
|
||||||
env.AddMethod(create_engine_file, "CreateEngineFile")
|
env.AddMethod(create_engine_file, "CreateEngineFile")
|
||||||
|
|
||||||
|
# Add method for getting the final zip path
|
||||||
|
env.AddMethod(get_template_zip_path, "GetTemplateZipPath")
|
||||||
|
|
||||||
# Add method for creating the final zip file
|
# Add method for creating the final zip file
|
||||||
env.AddMethod(create_template_zip, "CreateTemplateZip")
|
env.AddMethod(create_template_zip, "CreateTemplateZip")
|
||||||
|
|
||||||
|
@ -209,6 +213,7 @@ def configure(env: "Environment"):
|
||||||
stack_size_opt = "STACK_SIZE" if cc_semver >= (3, 1, 25) else "TOTAL_STACK"
|
stack_size_opt = "STACK_SIZE" if cc_semver >= (3, 1, 25) else "TOTAL_STACK"
|
||||||
env.Append(LINKFLAGS=["-s", "%s=%sKB" % (stack_size_opt, env["stack_size"])])
|
env.Append(LINKFLAGS=["-s", "%s=%sKB" % (stack_size_opt, env["stack_size"])])
|
||||||
|
|
||||||
|
if env["threads"]:
|
||||||
# Thread support (via SharedArrayBuffer).
|
# Thread support (via SharedArrayBuffer).
|
||||||
env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"])
|
env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"])
|
||||||
env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"])
|
env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"])
|
||||||
|
@ -216,6 +221,9 @@ def configure(env: "Environment"):
|
||||||
env.Append(LINKFLAGS=["-s", "DEFAULT_PTHREAD_STACK_SIZE=%sKB" % env["default_pthread_stack_size"]])
|
env.Append(LINKFLAGS=["-s", "DEFAULT_PTHREAD_STACK_SIZE=%sKB" % env["default_pthread_stack_size"]])
|
||||||
env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"])
|
env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"])
|
||||||
env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"])
|
env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"])
|
||||||
|
elif env["proxy_to_pthread"]:
|
||||||
|
print('"threads=no" support requires "proxy_to_pthread=no", disabling proxy to pthread.')
|
||||||
|
env["proxy_to_pthread"] = False
|
||||||
|
|
||||||
if env["lto"] != "none":
|
if env["lto"] != "none":
|
||||||
# Workaround https://github.com/emscripten-core/emscripten/issues/19781.
|
# Workaround https://github.com/emscripten-core/emscripten/issues/19781.
|
||||||
|
@ -224,7 +232,7 @@ def configure(env: "Environment"):
|
||||||
|
|
||||||
if env["dlink_enabled"]:
|
if env["dlink_enabled"]:
|
||||||
if env["proxy_to_pthread"]:
|
if env["proxy_to_pthread"]:
|
||||||
print("GDExtension support requires proxy_to_pthread=no, disabling")
|
print("GDExtension support requires proxy_to_pthread=no, disabling proxy to pthread.")
|
||||||
env["proxy_to_pthread"] = False
|
env["proxy_to_pthread"] = False
|
||||||
|
|
||||||
if cc_semver < (3, 1, 14):
|
if cc_semver < (3, 1, 14):
|
||||||
|
|
|
@ -47,6 +47,10 @@
|
||||||
</member>
|
</member>
|
||||||
<member name="variant/extensions_support" type="bool" setter="" getter="">
|
<member name="variant/extensions_support" type="bool" setter="" getter="">
|
||||||
</member>
|
</member>
|
||||||
|
<member name="variant/thread_support" type="bool" setter="" getter="">
|
||||||
|
If enabled, the exported game will support threads. It requires [url=https://web.dev/articles/coop-coep]a "cross-origin isolated" website[/url], which can be difficult to setup and brings some limitations (e.g. not being able to communicate with third-party websites).
|
||||||
|
If disabled, the exported game will not support threads. As a result, it is more prone to performance and audio issues, but will only require to be run on a HTTPS website.
|
||||||
|
</member>
|
||||||
<member name="vram_texture_compression/for_desktop" type="bool" setter="" getter="">
|
<member name="vram_texture_compression/for_desktop" type="bool" setter="" getter="">
|
||||||
</member>
|
</member>
|
||||||
<member name="vram_texture_compression/for_mobile" type="bool" setter="" getter="">
|
<member name="vram_texture_compression/for_mobile" type="bool" setter="" getter="">
|
||||||
|
|
|
@ -4,7 +4,12 @@ from SCons.Util import WhereIs
|
||||||
|
|
||||||
|
|
||||||
def run_closure_compiler(target, source, env, for_signature):
|
def run_closure_compiler(target, source, env, for_signature):
|
||||||
closure_bin = os.path.join(os.path.dirname(WhereIs("emcc")), "node_modules", ".bin", "google-closure-compiler")
|
closure_bin = os.path.join(
|
||||||
|
os.path.dirname(WhereIs("emcc")),
|
||||||
|
"node_modules",
|
||||||
|
".bin",
|
||||||
|
"google-closure-compiler",
|
||||||
|
)
|
||||||
cmd = [WhereIs("node"), closure_bin]
|
cmd = [WhereIs("node"), closure_bin]
|
||||||
cmd.extend(["--compilation_level", "ADVANCED_OPTIMIZATIONS"])
|
cmd.extend(["--compilation_level", "ADVANCED_OPTIMIZATIONS"])
|
||||||
for f in env["JSEXTERNS"]:
|
for f in env["JSEXTERNS"]:
|
||||||
|
@ -31,27 +36,29 @@ def get_build_version():
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|
||||||
def create_engine_file(env, target, source, externs):
|
def create_engine_file(env, target, source, externs, threads_enabled):
|
||||||
if env["use_closure_compiler"]:
|
if env["use_closure_compiler"]:
|
||||||
return env.BuildJS(target, source, JSEXTERNS=externs)
|
return env.BuildJS(target, source, JSEXTERNS=externs)
|
||||||
return env.Textfile(target, [env.File(s) for s in source])
|
subst_dict = {"___GODOT_THREADS_ENABLED": "true" if threads_enabled else "false"}
|
||||||
|
return env.Substfile(target=target, source=[env.File(s) for s in source], SUBST_DICT=subst_dict)
|
||||||
|
|
||||||
|
|
||||||
def create_template_zip(env, js, wasm, worker, side):
|
def create_template_zip(env, js, wasm, worker, side):
|
||||||
binary_name = "godot.editor" if env.editor_build else "godot"
|
binary_name = "godot.editor" if env.editor_build else "godot"
|
||||||
zip_dir = env.Dir("#bin/.web_zip")
|
zip_dir = env.Dir(env.GetTemplateZipPath())
|
||||||
in_files = [
|
in_files = [
|
||||||
js,
|
js,
|
||||||
wasm,
|
wasm,
|
||||||
worker,
|
|
||||||
"#platform/web/js/libs/audio.worklet.js",
|
"#platform/web/js/libs/audio.worklet.js",
|
||||||
]
|
]
|
||||||
out_files = [
|
out_files = [
|
||||||
zip_dir.File(binary_name + ".js"),
|
zip_dir.File(binary_name + ".js"),
|
||||||
zip_dir.File(binary_name + ".wasm"),
|
zip_dir.File(binary_name + ".wasm"),
|
||||||
zip_dir.File(binary_name + ".worker.js"),
|
|
||||||
zip_dir.File(binary_name + ".audio.worklet.js"),
|
zip_dir.File(binary_name + ".audio.worklet.js"),
|
||||||
]
|
]
|
||||||
|
if env["threads"]:
|
||||||
|
in_files.append(worker)
|
||||||
|
out_files.append(zip_dir.File(binary_name + ".worker.js"))
|
||||||
# Dynamic linking (extensions) specific.
|
# Dynamic linking (extensions) specific.
|
||||||
if env["dlink_enabled"]:
|
if env["dlink_enabled"]:
|
||||||
in_files.append(side) # Side wasm (contains the actual Godot code).
|
in_files.append(side) # Side wasm (contains the actual Godot code).
|
||||||
|
@ -65,18 +72,20 @@ def create_template_zip(env, js, wasm, worker, side):
|
||||||
"godot.editor.html",
|
"godot.editor.html",
|
||||||
"offline.html",
|
"offline.html",
|
||||||
"godot.editor.js",
|
"godot.editor.js",
|
||||||
"godot.editor.worker.js",
|
|
||||||
"godot.editor.audio.worklet.js",
|
"godot.editor.audio.worklet.js",
|
||||||
"logo.svg",
|
"logo.svg",
|
||||||
"favicon.png",
|
"favicon.png",
|
||||||
]
|
]
|
||||||
|
if env["threads"]:
|
||||||
|
cache.append("godot.editor.worker.js")
|
||||||
opt_cache = ["godot.editor.wasm"]
|
opt_cache = ["godot.editor.wasm"]
|
||||||
subst_dict = {
|
subst_dict = {
|
||||||
"@GODOT_VERSION@": get_build_version(),
|
"___GODOT_VERSION___": get_build_version(),
|
||||||
"@GODOT_NAME@": "GodotEngine",
|
"___GODOT_NAME___": "GodotEngine",
|
||||||
"@GODOT_CACHE@": json.dumps(cache),
|
"___GODOT_CACHE___": json.dumps(cache),
|
||||||
"@GODOT_OPT_CACHE@": json.dumps(opt_cache),
|
"___GODOT_OPT_CACHE___": json.dumps(opt_cache),
|
||||||
"@GODOT_OFFLINE_PAGE@": "offline.html",
|
"___GODOT_OFFLINE_PAGE___": "offline.html",
|
||||||
|
"___GODOT_THREADS_ENABLED___": "true" if env["threads"] else "false",
|
||||||
}
|
}
|
||||||
html = env.Substfile(target="#bin/godot${PROGSUFFIX}.html", source=html, SUBST_DICT=subst_dict)
|
html = env.Substfile(target="#bin/godot${PROGSUFFIX}.html", source=html, SUBST_DICT=subst_dict)
|
||||||
in_files.append(html)
|
in_files.append(html)
|
||||||
|
@ -88,7 +97,9 @@ def create_template_zip(env, js, wasm, worker, side):
|
||||||
out_files.append(zip_dir.File("favicon.png"))
|
out_files.append(zip_dir.File("favicon.png"))
|
||||||
# PWA
|
# PWA
|
||||||
service_worker = env.Substfile(
|
service_worker = env.Substfile(
|
||||||
target="#bin/godot${PROGSUFFIX}.service.worker.js", source=service_worker, SUBST_DICT=subst_dict
|
target="#bin/godot${PROGSUFFIX}.service.worker.js",
|
||||||
|
source=service_worker,
|
||||||
|
SUBST_DICT=subst_dict,
|
||||||
)
|
)
|
||||||
in_files.append(service_worker)
|
in_files.append(service_worker)
|
||||||
out_files.append(zip_dir.File("service.worker.js"))
|
out_files.append(zip_dir.File("service.worker.js"))
|
||||||
|
@ -115,6 +126,10 @@ def create_template_zip(env, js, wasm, worker, side):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_template_zip_path(env):
|
||||||
|
return "#bin/.web_zip"
|
||||||
|
|
||||||
|
|
||||||
def add_js_libraries(env, libraries):
|
def add_js_libraries(env, libraries):
|
||||||
env.Append(JS_LIBS=env.File(libraries))
|
env.Append(JS_LIBS=env.File(libraries))
|
||||||
|
|
||||||
|
|
|
@ -169,6 +169,13 @@ void EditorExportPlatformWeb::_fix_html(Vector<uint8_t> &p_html, const Ref<Edito
|
||||||
replaces["$GODOT_PROJECT_NAME"] = GLOBAL_GET("application/config/name");
|
replaces["$GODOT_PROJECT_NAME"] = GLOBAL_GET("application/config/name");
|
||||||
replaces["$GODOT_HEAD_INCLUDE"] = head_include + custom_head_include;
|
replaces["$GODOT_HEAD_INCLUDE"] = head_include + custom_head_include;
|
||||||
replaces["$GODOT_CONFIG"] = str_config;
|
replaces["$GODOT_CONFIG"] = str_config;
|
||||||
|
|
||||||
|
if (p_preset->get("variant/thread_support")) {
|
||||||
|
replaces["$GODOT_THREADS_ENABLED"] = "true";
|
||||||
|
} else {
|
||||||
|
replaces["$GODOT_THREADS_ENABLED"] = "false";
|
||||||
|
}
|
||||||
|
|
||||||
_replace_strings(replaces, p_html);
|
_replace_strings(replaces, p_html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,9 +223,9 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
|
||||||
const String name = p_path.get_file().get_basename();
|
const String name = p_path.get_file().get_basename();
|
||||||
bool extensions = (bool)p_preset->get("variant/extensions_support");
|
bool extensions = (bool)p_preset->get("variant/extensions_support");
|
||||||
HashMap<String, String> replaces;
|
HashMap<String, String> replaces;
|
||||||
replaces["@GODOT_VERSION@"] = String::num_int64(OS::get_singleton()->get_unix_time()) + "|" + String::num_int64(OS::get_singleton()->get_ticks_usec());
|
replaces["___GODOT_VERSION___"] = String::num_int64(OS::get_singleton()->get_unix_time()) + "|" + String::num_int64(OS::get_singleton()->get_ticks_usec());
|
||||||
replaces["@GODOT_NAME@"] = proj_name.substr(0, 16);
|
replaces["___GODOT_NAME___"] = proj_name.substr(0, 16);
|
||||||
replaces["@GODOT_OFFLINE_PAGE@"] = name + ".offline.html";
|
replaces["___GODOT_OFFLINE_PAGE___"] = name + ".offline.html";
|
||||||
|
|
||||||
// Files cached during worker install.
|
// Files cached during worker install.
|
||||||
Array cache_files;
|
Array cache_files;
|
||||||
|
@ -231,7 +238,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
|
||||||
}
|
}
|
||||||
cache_files.push_back(name + ".worker.js");
|
cache_files.push_back(name + ".worker.js");
|
||||||
cache_files.push_back(name + ".audio.worklet.js");
|
cache_files.push_back(name + ".audio.worklet.js");
|
||||||
replaces["@GODOT_CACHE@"] = Variant(cache_files).to_json_string();
|
replaces["___GODOT_CACHE___"] = Variant(cache_files).to_json_string();
|
||||||
|
|
||||||
// Heavy files that are cached on demand.
|
// Heavy files that are cached on demand.
|
||||||
Array opt_cache_files;
|
Array opt_cache_files;
|
||||||
|
@ -243,7 +250,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
|
||||||
opt_cache_files.push_back(p_shared_objects[i].path.get_file());
|
opt_cache_files.push_back(p_shared_objects[i].path.get_file());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
replaces["@GODOT_OPT_CACHE@"] = Variant(opt_cache_files).to_json_string();
|
replaces["___GODOT_OPT_CACHE___"] = Variant(opt_cache_files).to_json_string();
|
||||||
|
|
||||||
const String sw_path = dir.path_join(name + ".service.worker.js");
|
const String sw_path = dir.path_join(name + ".service.worker.js");
|
||||||
Vector<uint8_t> sw;
|
Vector<uint8_t> sw;
|
||||||
|
@ -335,6 +342,7 @@ void EditorExportPlatformWeb::get_export_options(List<ExportOption> *r_options)
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
|
||||||
|
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "variant/extensions_support"), false)); // Export type.
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "variant/extensions_support"), false)); // Export type.
|
||||||
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "variant/thread_support"), true)); // Thread support (i.e. run with or without COEP/COOP headers).
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer
|
||||||
|
|
||||||
|
@ -377,10 +385,11 @@ bool EditorExportPlatformWeb::has_valid_export_configuration(const Ref<EditorExp
|
||||||
String err;
|
String err;
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
bool extensions = (bool)p_preset->get("variant/extensions_support");
|
bool extensions = (bool)p_preset->get("variant/extensions_support");
|
||||||
|
bool thread_support = (bool)p_preset->get("variant/thread_support");
|
||||||
|
|
||||||
// Look for export templates (first official, and if defined custom templates).
|
// Look for export templates (first official, and if defined custom templates).
|
||||||
bool dvalid = exists_export_template(_get_template_name(extensions, true), &err);
|
bool dvalid = exists_export_template(_get_template_name(extensions, thread_support, true), &err);
|
||||||
bool rvalid = exists_export_template(_get_template_name(extensions, false), &err);
|
bool rvalid = exists_export_template(_get_template_name(extensions, thread_support, false), &err);
|
||||||
|
|
||||||
if (p_preset->get("custom_template/debug") != "") {
|
if (p_preset->get("custom_template/debug") != "") {
|
||||||
dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
|
dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
|
||||||
|
@ -454,7 +463,8 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
|
||||||
template_path = template_path.strip_edges();
|
template_path = template_path.strip_edges();
|
||||||
if (template_path.is_empty()) {
|
if (template_path.is_empty()) {
|
||||||
bool extensions = (bool)p_preset->get("variant/extensions_support");
|
bool extensions = (bool)p_preset->get("variant/extensions_support");
|
||||||
template_path = find_export_template(_get_template_name(extensions, p_debug));
|
bool thread_support = (bool)p_preset->get("variant/thread_support");
|
||||||
|
template_path = find_export_template(_get_template_name(extensions, thread_support, p_debug));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!template_path.is_empty() && !FileAccess::exists(template_path)) {
|
if (!template_path.is_empty() && !FileAccess::exists(template_path)) {
|
||||||
|
|
|
@ -56,11 +56,14 @@ class EditorExportPlatformWeb : public EditorExportPlatform {
|
||||||
Mutex server_lock;
|
Mutex server_lock;
|
||||||
Thread server_thread;
|
Thread server_thread;
|
||||||
|
|
||||||
String _get_template_name(bool p_extension, bool p_debug) const {
|
String _get_template_name(bool p_extension, bool p_thread_support, bool p_debug) const {
|
||||||
String name = "web";
|
String name = "web";
|
||||||
if (p_extension) {
|
if (p_extension) {
|
||||||
name += "_dlink";
|
name += "_dlink";
|
||||||
}
|
}
|
||||||
|
if (!p_thread_support) {
|
||||||
|
name += "_nothreads";
|
||||||
|
}
|
||||||
if (p_debug) {
|
if (p_debug) {
|
||||||
name += "_debug.zip";
|
name += "_debug.zip";
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -72,8 +72,14 @@ const Features = { // eslint-disable-line no-unused-vars
|
||||||
*
|
*
|
||||||
* @returns {Array<string>} A list of human-readable missing features.
|
* @returns {Array<string>} A list of human-readable missing features.
|
||||||
* @function Engine.getMissingFeatures
|
* @function Engine.getMissingFeatures
|
||||||
|
* @typedef {{ threads: boolean }} SupportedFeatures
|
||||||
|
* @param {SupportedFeatures} supportedFeatures
|
||||||
*/
|
*/
|
||||||
getMissingFeatures: function () {
|
getMissingFeatures: function (supportedFeatures = {}) {
|
||||||
|
const {
|
||||||
|
threads: supportsThreads = true,
|
||||||
|
} = supportedFeatures;
|
||||||
|
|
||||||
const missing = [];
|
const missing = [];
|
||||||
if (!Features.isWebGLAvailable(2)) {
|
if (!Features.isWebGLAvailable(2)) {
|
||||||
missing.push('WebGL2 - Check web browser configuration and hardware support');
|
missing.push('WebGL2 - Check web browser configuration and hardware support');
|
||||||
|
@ -84,12 +90,16 @@ const Features = { // eslint-disable-line no-unused-vars
|
||||||
if (!Features.isSecureContext()) {
|
if (!Features.isSecureContext()) {
|
||||||
missing.push('Secure Context - Check web server configuration (use HTTPS)');
|
missing.push('Secure Context - Check web server configuration (use HTTPS)');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (supportsThreads) {
|
||||||
if (!Features.isCrossOriginIsolated()) {
|
if (!Features.isCrossOriginIsolated()) {
|
||||||
missing.push('Cross Origin Isolation - Check web server configuration (send correct headers)');
|
missing.push('Cross-Origin Isolation - Check that the web server configuration sends the correct headers.');
|
||||||
}
|
}
|
||||||
if (!Features.isSharedArrayBufferAvailable()) {
|
if (!Features.isSharedArrayBufferAvailable()) {
|
||||||
missing.push('SharedArrayBuffer - Check web server configuration (send correct headers)');
|
missing.push('SharedArrayBuffer - Check that the web server configuration sends the correct headers.');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Audio is normally optional since we have a dummy fallback.
|
// Audio is normally optional since we have a dummy fallback.
|
||||||
return missing;
|
return missing;
|
||||||
},
|
},
|
||||||
|
|
|
@ -167,7 +167,7 @@ class GodotProcessor extends AudioWorkletProcessor {
|
||||||
GodotProcessor.write_input(this.input_buffer, input);
|
GodotProcessor.write_input(this.input_buffer, input);
|
||||||
this.input.write(this.input_buffer);
|
this.input.write(this.input_buffer);
|
||||||
} else {
|
} else {
|
||||||
this.port.postMessage('Input buffer is full! Skipping input frame.');
|
// this.port.postMessage('Input buffer is full! Skipping input frame.'); // Uncomment this line to debug input buffer.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const process_output = GodotProcessor.array_has_data(outputs);
|
const process_output = GodotProcessor.array_has_data(outputs);
|
||||||
|
@ -184,7 +184,7 @@ class GodotProcessor extends AudioWorkletProcessor {
|
||||||
this.port.postMessage({ 'cmd': 'read', 'data': chunk });
|
this.port.postMessage({ 'cmd': 'read', 'data': chunk });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.port.postMessage('Output buffer has not enough frames! Skipping output frame.');
|
// this.port.postMessage('Output buffer has not enough frames! Skipping output frame.'); // Uncomment this line to debug output buffer.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.process_notify();
|
this.process_notify();
|
||||||
|
|
|
@ -503,7 +503,9 @@ void HTTPRequest::_notification(int p_what) {
|
||||||
|
|
||||||
void HTTPRequest::set_use_threads(bool p_use) {
|
void HTTPRequest::set_use_threads(bool p_use) {
|
||||||
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
|
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
use_threads.set_to(p_use);
|
use_threads.set_to(p_use);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HTTPRequest::is_using_threads() const {
|
bool HTTPRequest::is_using_threads() const {
|
||||||
|
|
|
@ -88,7 +88,11 @@
|
||||||
ShaderTypes *shader_types = nullptr;
|
ShaderTypes *shader_types = nullptr;
|
||||||
|
|
||||||
static PhysicsServer3D *_createGodotPhysics3DCallback() {
|
static PhysicsServer3D *_createGodotPhysics3DCallback() {
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
bool using_threads = GLOBAL_GET("physics/3d/run_on_separate_thread");
|
bool using_threads = GLOBAL_GET("physics/3d/run_on_separate_thread");
|
||||||
|
#else
|
||||||
|
bool using_threads = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
PhysicsServer3D *physics_server_3d = memnew(GodotPhysicsServer3D(using_threads));
|
PhysicsServer3D *physics_server_3d = memnew(GodotPhysicsServer3D(using_threads));
|
||||||
|
|
||||||
|
@ -96,7 +100,11 @@ static PhysicsServer3D *_createGodotPhysics3DCallback() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static PhysicsServer2D *_createGodotPhysics2DCallback() {
|
static PhysicsServer2D *_createGodotPhysics2DCallback() {
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
bool using_threads = GLOBAL_GET("physics/2d/run_on_separate_thread");
|
bool using_threads = GLOBAL_GET("physics/2d/run_on_separate_thread");
|
||||||
|
#else
|
||||||
|
bool using_threads = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
PhysicsServer2D *physics_server_2d = memnew(GodotPhysicsServer2D(using_threads));
|
PhysicsServer2D *physics_server_2d = memnew(GodotPhysicsServer2D(using_threads));
|
||||||
|
|
||||||
|
|
|
@ -395,15 +395,19 @@ RenderingServerDefault::RenderingServerDefault(bool p_create_thread) :
|
||||||
command_queue(p_create_thread) {
|
command_queue(p_create_thread) {
|
||||||
RenderingServer::init();
|
RenderingServer::init();
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
create_thread = p_create_thread;
|
create_thread = p_create_thread;
|
||||||
|
if (!create_thread) {
|
||||||
if (!p_create_thread) {
|
|
||||||
server_thread = Thread::get_caller_id();
|
server_thread = Thread::get_caller_id();
|
||||||
} else {
|
} else {
|
||||||
server_thread = 0;
|
server_thread = 0;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
create_thread = false;
|
||||||
|
server_thread = Thread::get_main_id();
|
||||||
|
#endif
|
||||||
|
RSG::threaded = create_thread;
|
||||||
|
|
||||||
RSG::threaded = p_create_thread;
|
|
||||||
RSG::canvas = memnew(RendererCanvasCull);
|
RSG::canvas = memnew(RendererCanvasCull);
|
||||||
RSG::viewport = memnew(RendererViewport);
|
RSG::viewport = memnew(RendererViewport);
|
||||||
RendererSceneCull *sr = memnew(RendererSceneCull);
|
RendererSceneCull *sr = memnew(RendererSceneCull);
|
||||||
|
|
|
@ -78,7 +78,7 @@ class RenderingServerDefault : public RenderingServer {
|
||||||
static void _thread_callback(void *_instance);
|
static void _thread_callback(void *_instance);
|
||||||
void _thread_loop();
|
void _thread_loop();
|
||||||
|
|
||||||
Thread::ID server_thread;
|
Thread::ID server_thread = 0;
|
||||||
SafeFlag exit;
|
SafeFlag exit;
|
||||||
Thread thread;
|
Thread thread;
|
||||||
SafeFlag draw_thread_up;
|
SafeFlag draw_thread_up;
|
||||||
|
|
Loading…
Reference in New Issue