diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/io/resource_loader.cpp | 37 | ||||
-rw-r--r-- | core/io/resource_loader.h | 3 | ||||
-rw-r--r-- | core/math/transform_interpolator.cpp | 34 | ||||
-rw-r--r-- | core/object/worker_thread_pool.cpp | 91 | ||||
-rw-r--r-- | core/object/worker_thread_pool.h | 24 | ||||
-rw-r--r-- | core/templates/command_queue_mt.h | 4 | ||||
-rw-r--r-- | core/variant/variant.cpp | 2 | ||||
-rw-r--r-- | core/variant/variant_op.h | 32 |
8 files changed, 151 insertions, 76 deletions
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index ed5e482296..58ad61b621 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -40,6 +40,7 @@ #include "core/string/print_string.h" #include "core/string/translation.h" #include "core/variant/variant_parser.h" +#include "servers/rendering_server.h" #ifdef DEBUG_LOAD_THREADED #define print_lt(m_text) print_line(m_text) @@ -585,6 +586,16 @@ ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const *r_progress = _dependency_get_progress(local_path); } + // Support userland polling in a loop on the main thread. + if (Thread::is_main_thread() && status == THREAD_LOAD_IN_PROGRESS) { + uint64_t frame = Engine::get_singleton()->get_process_frames(); + if (frame == load_task.last_progress_check_main_thread_frame) { + _ensure_load_progress(); + } else { + load_task.last_progress_check_main_thread_frame = frame; + } + } + return status; } @@ -613,6 +624,21 @@ Ref<Resource> ResourceLoader::load_threaded_get(const String &p_path, Error *r_e } return Ref<Resource>(); } + + // Support userland requesting on the main thread before the load is reported to be complete. + if (Thread::is_main_thread() && !load_token->local_path.is_empty()) { + const ThreadLoadTask &load_task = thread_load_tasks[load_token->local_path]; + while (load_task.status == THREAD_LOAD_IN_PROGRESS) { + if (!_ensure_load_progress()) { + // This local poll loop is not needed. + break; + } + thread_load_lock.~MutexLock(); + OS::get_singleton()->delay_usec(1000); + new (&thread_load_lock) MutexLock(thread_load_mutex); + } + } + res = _load_complete_inner(*load_token, r_error, thread_load_lock); if (load_token->unreference()) { memdelete(load_token); @@ -731,6 +757,17 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro } } +bool ResourceLoader::_ensure_load_progress() { + // Some servers may need a new engine iteration to allow the load to progress. + // Since the only known one is the rendering server (in single thread mode), let's keep it simple and just sync it. + // This may be refactored in the future to support other servers and have less coupling. + if (OS::get_singleton()->get_render_thread_mode() == OS::RENDER_SEPARATE_THREAD) { + return false; // Not needed. + } + RenderingServer::get_singleton()->sync(); + return true; +} + Ref<Resource> ResourceLoader::ensure_resource_ref_override_for_outer_load(const String &p_path, const String &p_res_type) { ERR_FAIL_COND_V(load_nesting == 0, Ref<Resource>()); // It makes no sense to use this from nesting level 0. const String &local_path = _validate_local_path(p_path); diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index c48f39b5cc..46df79ea22 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -174,6 +174,7 @@ private: String type_hint; float progress = 0.0f; float max_reported_progress = 0.0f; + uint64_t last_progress_check_main_thread_frame = UINT64_MAX; ThreadLoadStatus status = THREAD_LOAD_IN_PROGRESS; ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE; Error error = OK; @@ -197,6 +198,8 @@ private: static float _dependency_get_progress(const String &p_path); + static bool _ensure_load_progress(); + public: static Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE); static ThreadLoadStatus load_threaded_get_status(const String &p_path, float *r_progress = nullptr); diff --git a/core/math/transform_interpolator.cpp b/core/math/transform_interpolator.cpp index 7cfe880b5a..6a564b0ca7 100644 --- a/core/math/transform_interpolator.cpp +++ b/core/math/transform_interpolator.cpp @@ -33,44 +33,14 @@ #include "core/math/transform_2d.h" void TransformInterpolator::interpolate_transform_2d(const Transform2D &p_prev, const Transform2D &p_curr, Transform2D &r_result, real_t p_fraction) { - // Extract parameters. - Vector2 p1 = p_prev.get_origin(); - Vector2 p2 = p_curr.get_origin(); - // Special case for physics interpolation, if flipping, don't interpolate basis. // If the determinant polarity changes, the handedness of the coordinate system changes. if (_sign(p_prev.determinant()) != _sign(p_curr.determinant())) { r_result.columns[0] = p_curr.columns[0]; r_result.columns[1] = p_curr.columns[1]; - r_result.set_origin(p1.lerp(p2, p_fraction)); + r_result.set_origin(p_prev.get_origin().lerp(p_curr.get_origin(), p_fraction)); return; } - real_t r1 = p_prev.get_rotation(); - real_t r2 = p_curr.get_rotation(); - - Size2 s1 = p_prev.get_scale(); - Size2 s2 = p_curr.get_scale(); - - // Slerp rotation. - Vector2 v1(Math::cos(r1), Math::sin(r1)); - Vector2 v2(Math::cos(r2), Math::sin(r2)); - - real_t dot = v1.dot(v2); - - dot = CLAMP(dot, -1, 1); - - Vector2 v; - - if (dot > 0.9995f) { - v = v1.lerp(v2, p_fraction).normalized(); // Linearly interpolate to avoid numerical precision issues. - } else { - real_t angle = p_fraction * Math::acos(dot); - Vector2 v3 = (v2 - v1 * dot).normalized(); - v = v1 * Math::cos(angle) + v3 * Math::sin(angle); - } - - // Construct matrix. - r_result = Transform2D(Math::atan2(v.y, v.x), p1.lerp(p2, p_fraction)); - r_result.scale_basis(s1.lerp(s2, p_fraction)); + r_result = p_prev.interpolate_with(p_curr, p_fraction); } diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp index a7c0a0353e..caf4ed3835 100644 --- a/core/object/worker_thread_pool.cpp +++ b/core/object/worker_thread_pool.cpp @@ -33,7 +33,6 @@ #include "core/object/script_language.h" #include "core/os/os.h" #include "core/os/thread_safe.h" -#include "core/templates/command_queue_mt.h" WorkerThreadPool::Task *const WorkerThreadPool::ThreadData::YIELDING = (Task *)1; @@ -46,7 +45,9 @@ void WorkerThreadPool::Task::free_template_userdata() { WorkerThreadPool *WorkerThreadPool::singleton = nullptr; -thread_local CommandQueueMT *WorkerThreadPool::flushing_cmd_queue = nullptr; +#ifdef THREADS_ENABLED +thread_local uintptr_t WorkerThreadPool::unlockable_mutexes[MAX_UNLOCKABLE_MUTEXES] = {}; +#endif void WorkerThreadPool::_process_task(Task *p_task) { #ifdef THREADS_ENABLED @@ -419,6 +420,34 @@ Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) { return OK; } +void WorkerThreadPool::_lock_unlockable_mutexes() { +#ifdef THREADS_ENABLED + for (uint32_t i = 0; i < MAX_UNLOCKABLE_MUTEXES; i++) { + if (unlockable_mutexes[i]) { + if ((((uintptr_t)unlockable_mutexes[i]) & 1) == 0) { + ((Mutex *)unlockable_mutexes[i])->lock(); + } else { + ((BinaryMutex *)(unlockable_mutexes[i] & ~1))->lock(); + } + } + } +#endif +} + +void WorkerThreadPool::_unlock_unlockable_mutexes() { +#ifdef THREADS_ENABLED + for (uint32_t i = 0; i < MAX_UNLOCKABLE_MUTEXES; i++) { + if (unlockable_mutexes[i]) { + if ((((uintptr_t)unlockable_mutexes[i]) & 1) == 0) { + ((Mutex *)unlockable_mutexes[i])->unlock(); + } else { + ((BinaryMutex *)(unlockable_mutexes[i] & ~1))->unlock(); + } + } + } +#endif +} + void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task) { // Keep processing tasks until the condition to stop waiting is met. @@ -426,6 +455,7 @@ void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, T while (true) { Task *task_to_process = nullptr; + bool relock_unlockables = false; { MutexLock lock(task_mutex); bool was_signaled = p_caller_pool_thread->signaled; @@ -463,13 +493,9 @@ void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, T if (!task_to_process) { p_caller_pool_thread->awaited_task = p_task; - if (flushing_cmd_queue) { - flushing_cmd_queue->unlock(); - } + _unlock_unlockable_mutexes(); + relock_unlockables = true; p_caller_pool_thread->cond_var.wait(lock); - if (flushing_cmd_queue) { - flushing_cmd_queue->lock(); - } DEV_ASSERT(exit_threads || p_caller_pool_thread->signaled || IS_WAIT_OVER); p_caller_pool_thread->awaited_task = nullptr; @@ -477,6 +503,10 @@ void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, T } } + if (relock_unlockables) { + _lock_unlockable_mutexes(); + } + if (task_to_process) { _process_task(task_to_process); } @@ -603,13 +633,9 @@ void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) { { Group *group = *groupp; - if (flushing_cmd_queue) { - flushing_cmd_queue->unlock(); - } + _unlock_unlockable_mutexes(); group->done_semaphore.wait(); - if (flushing_cmd_queue) { - flushing_cmd_queue->lock(); - } + _lock_unlockable_mutexes(); uint32_t max_users = group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment. uint32_t finished_users = group->finished.increment(); // fetch happens before inc, so increment later. @@ -633,16 +659,41 @@ int WorkerThreadPool::get_thread_index() { return singleton->thread_ids.has(tid) ? singleton->thread_ids[tid] : -1; } -void WorkerThreadPool::thread_enter_command_queue_mt_flush(CommandQueueMT *p_queue) { - ERR_FAIL_COND(flushing_cmd_queue != nullptr); - flushing_cmd_queue = p_queue; +#ifdef THREADS_ENABLED +uint32_t WorkerThreadPool::thread_enter_unlock_allowance_zone(Mutex *p_mutex) { + return _thread_enter_unlock_allowance_zone(p_mutex, false); +} + +uint32_t WorkerThreadPool::thread_enter_unlock_allowance_zone(BinaryMutex *p_mutex) { + return _thread_enter_unlock_allowance_zone(p_mutex, true); } -void WorkerThreadPool::thread_exit_command_queue_mt_flush() { - ERR_FAIL_NULL(flushing_cmd_queue); - flushing_cmd_queue = nullptr; +uint32_t WorkerThreadPool::_thread_enter_unlock_allowance_zone(void *p_mutex, bool p_is_binary) { + for (uint32_t i = 0; i < MAX_UNLOCKABLE_MUTEXES; i++) { + if (unlikely(unlockable_mutexes[i] == (uintptr_t)p_mutex)) { + // Already registered in the current thread. + return UINT32_MAX; + } + if (!unlockable_mutexes[i]) { + unlockable_mutexes[i] = (uintptr_t)p_mutex; + if (p_is_binary) { + unlockable_mutexes[i] |= 1; + } + return i; + } + } + ERR_FAIL_V_MSG(UINT32_MAX, "No more unlockable mutex slots available. Engine bug."); } +void WorkerThreadPool::thread_exit_unlock_allowance_zone(uint32_t p_zone_id) { + if (p_zone_id == UINT32_MAX) { + return; + } + DEV_ASSERT(unlockable_mutexes[p_zone_id]); + unlockable_mutexes[p_zone_id] = 0; +} +#endif + void WorkerThreadPool::init(int p_thread_count, float p_low_priority_task_ratio) { ERR_FAIL_COND(threads.size() > 0); if (p_thread_count < 0) { diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h index a9cf260a0f..8774143abf 100644 --- a/core/object/worker_thread_pool.h +++ b/core/object/worker_thread_pool.h @@ -41,8 +41,6 @@ #include "core/templates/rid.h" #include "core/templates/safe_refcount.h" -class CommandQueueMT; - class WorkerThreadPool : public Object { GDCLASS(WorkerThreadPool, Object) public: @@ -163,7 +161,10 @@ private: static WorkerThreadPool *singleton; - static thread_local CommandQueueMT *flushing_cmd_queue; +#ifdef THREADS_ENABLED + static const uint32_t MAX_UNLOCKABLE_MUTEXES = 2; + static thread_local uintptr_t unlockable_mutexes[MAX_UNLOCKABLE_MUTEXES]; +#endif TaskID _add_task(const Callable &p_callable, void (*p_func)(void *), void *p_userdata, BaseTemplateUserdata *p_template_userdata, bool p_high_priority, const String &p_description); GroupID _add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description); @@ -190,6 +191,13 @@ private: void _wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task); +#ifdef THREADS_ENABLED + static uint32_t _thread_enter_unlock_allowance_zone(void *p_mutex, bool p_is_binary); +#endif + + void _lock_unlockable_mutexes(); + void _unlock_unlockable_mutexes(); + protected: static void _bind_methods(); @@ -232,8 +240,14 @@ public: static WorkerThreadPool *get_singleton() { return singleton; } static int get_thread_index(); - static void thread_enter_command_queue_mt_flush(CommandQueueMT *p_queue); - static void thread_exit_command_queue_mt_flush(); +#ifdef THREADS_ENABLED + static uint32_t thread_enter_unlock_allowance_zone(Mutex *p_mutex); + static uint32_t thread_enter_unlock_allowance_zone(BinaryMutex *p_mutex); + static void thread_exit_unlock_allowance_zone(uint32_t p_zone_id); +#else + static uint32_t thread_enter_unlock_allowance_zone(void *p_mutex) { return UINT32_MAX; } + static void thread_exit_unlock_allowance_zone(uint32_t p_zone_id) {} +#endif void init(int p_thread_count = -1, float p_low_priority_task_ratio = 0.3); void finish(); diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h index 349404d75b..0748e9cb83 100644 --- a/core/templates/command_queue_mt.h +++ b/core/templates/command_queue_mt.h @@ -364,7 +364,7 @@ class CommandQueueMT { lock(); - WorkerThreadPool::thread_enter_command_queue_mt_flush(this); + uint32_t allowance_id = WorkerThreadPool::thread_enter_unlock_allowance_zone(&mutex); while (flush_read_ptr < command_mem.size()) { uint64_t size = *(uint64_t *)&command_mem[flush_read_ptr]; flush_read_ptr += 8; @@ -383,7 +383,7 @@ class CommandQueueMT { flush_read_ptr += size; } - WorkerThreadPool::thread_exit_command_queue_mt_flush(); + WorkerThreadPool::thread_exit_unlock_allowance_zone(allowance_id); command_mem.clear(); flush_read_ptr = 0; diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index 30a8facd67..c1ef31c784 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -951,7 +951,7 @@ bool Variant::is_zero() const { return *reinterpret_cast<const ::RID *>(_data._mem) == ::RID(); } case OBJECT: { - return get_validated_object() == nullptr; + return _get_obj().obj == nullptr; } case CALLABLE: { return reinterpret_cast<const Callable *>(_data._mem)->is_null(); diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h index 0b94d79a97..ac39a4135f 100644 --- a/core/variant/variant_op.h +++ b/core/variant/variant_op.h @@ -548,14 +548,14 @@ public: class OperatorEvaluatorEqualObject { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - const ObjectID &a = VariantInternal::get_object_id(&p_left); - const ObjectID &b = VariantInternal::get_object_id(&p_right); + const Object *a = p_left.get_validated_object(); + const Object *b = p_right.get_validated_object(); *r_ret = a == b; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - const ObjectID &a = VariantInternal::get_object_id(left); - const ObjectID &b = VariantInternal::get_object_id(right); + const Object *a = left->get_validated_object(); + const Object *b = right->get_validated_object(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == b; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -567,12 +567,12 @@ public: class OperatorEvaluatorEqualObjectNil { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - const Object *a = p_left.operator Object *(); + const Object *a = p_left.get_validated_object(); *r_ret = a == nullptr; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - const Object *a = left->operator Object *(); + const Object *a = left->get_validated_object(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == nullptr; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -584,12 +584,12 @@ public: class OperatorEvaluatorEqualNilObject { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - const Object *b = p_right.operator Object *(); + const Object *b = p_right.get_validated_object(); *r_ret = nullptr == b; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - const Object *b = right->operator Object *(); + const Object *b = right->get_validated_object(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr == b; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -619,14 +619,14 @@ public: class OperatorEvaluatorNotEqualObject { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - const ObjectID &a = VariantInternal::get_object_id(&p_left); - const ObjectID &b = VariantInternal::get_object_id(&p_right); + Object *a = p_left.get_validated_object(); + Object *b = p_right.get_validated_object(); *r_ret = a != b; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - const ObjectID &a = VariantInternal::get_object_id(left); - const ObjectID &b = VariantInternal::get_object_id(right); + Object *a = left->get_validated_object(); + Object *b = right->get_validated_object(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != b; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -638,12 +638,12 @@ public: class OperatorEvaluatorNotEqualObjectNil { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - Object *a = p_left.operator Object *(); + Object *a = p_left.get_validated_object(); *r_ret = a != nullptr; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - Object *a = left->operator Object *(); + Object *a = left->get_validated_object(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != nullptr; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -655,12 +655,12 @@ public: class OperatorEvaluatorNotEqualNilObject { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - Object *b = p_right.operator Object *(); + Object *b = p_right.get_validated_object(); *r_ret = nullptr != b; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - Object *b = right->operator Object *(); + Object *b = right->get_validated_object(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr != b; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { |