diff --git a/core/config/engine.cpp b/core/config/engine.cpp index 9f4bff3779..f2f8aebe8b 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -82,6 +82,17 @@ int Engine::get_audio_output_latency() const { return _audio_output_latency; } +void Engine::increment_frames_drawn() { + if (frame_server_synced) { + server_syncs++; + } else { + server_syncs = 0; + } + frame_server_synced = false; + + frames_drawn++; +} + uint64_t Engine::get_frames_drawn() { return frames_drawn; } @@ -364,6 +375,11 @@ Engine *Engine::get_singleton() { return singleton; } +bool Engine::notify_frame_server_synced() { + frame_server_synced = true; + return server_syncs > SERVER_SYNC_FRAME_COUNT_WARNING; +} + Engine::Engine() { singleton = this; } diff --git a/core/config/engine.h b/core/config/engine.h index d1495b36c2..8dece803e3 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -91,6 +91,10 @@ private: String write_movie_path; String shader_cache_path; + static constexpr int SERVER_SYNC_FRAME_COUNT_WARNING = 5; + int server_syncs = 0; + bool frame_server_synced = false; + public: static Engine *get_singleton(); @@ -179,6 +183,9 @@ public: bool is_generate_spirv_debug_info_enabled() const; int32_t get_gpu_index() const; + void increment_frames_drawn(); + bool notify_frame_server_synced(); + Engine(); virtual ~Engine() {} }; diff --git a/core/templates/command_queue_mt.cpp b/core/templates/command_queue_mt.cpp index 6ecd75ebc1..0c5c6394a1 100644 --- a/core/templates/command_queue_mt.cpp +++ b/core/templates/command_queue_mt.cpp @@ -70,14 +70,8 @@ CommandQueueMT::SyncSemaphore *CommandQueueMT::_alloc_sync_sem() { return &sync_sems[idx]; } -CommandQueueMT::CommandQueueMT(bool p_sync) { - if (p_sync) { - sync = memnew(Semaphore); - } +CommandQueueMT::CommandQueueMT() { } CommandQueueMT::~CommandQueueMT() { - if (sync) { - memdelete(sync); - } } diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h index 4056119851..c0740e9352 100644 --- a/core/templates/command_queue_mt.h +++ b/core/templates/command_queue_mt.h @@ -248,16 +248,17 @@ #define CMD_TYPE(N) Command##N #define CMD_ASSIGN_PARAM(N) cmd->p##N = p##N -#define DECL_PUSH(N) \ - template \ - void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \ - CMD_TYPE(N) *cmd = allocate_and_lock(); \ - cmd->instance = p_instance; \ - cmd->method = p_method; \ - SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ - unlock(); \ - if (sync) \ - sync->post(); \ +#define DECL_PUSH(N) \ + template \ + void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \ + CMD_TYPE(N) *cmd = allocate_and_lock(); \ + cmd->instance = p_instance; \ + cmd->method = p_method; \ + SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ + if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \ + WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \ + } \ + unlock(); \ } #define CMD_RET_TYPE(N) CommandRet##N @@ -272,9 +273,10 @@ SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ cmd->ret = r_ret; \ cmd->sync_sem = ss; \ + if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \ + WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \ + } \ unlock(); \ - if (sync) \ - sync->post(); \ ss->sem.wait(); \ ss->in_use = false; \ } @@ -290,9 +292,10 @@ cmd->method = p_method; \ SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ cmd->sync_sem = ss; \ + if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \ + WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \ + } \ unlock(); \ - if (sync) \ - sync->post(); \ ss->sem.wait(); \ ss->in_use = false; \ } @@ -340,7 +343,7 @@ class CommandQueueMT { LocalVector command_mem; SyncSemaphore sync_sems[SYNC_SEMAPHORES]; Mutex mutex; - Semaphore *sync = nullptr; + WorkerThreadPool::TaskID pump_task_id = WorkerThreadPool::INVALID_TASK_ID; uint64_t flush_read_ptr = 0; template @@ -421,12 +424,16 @@ public: } void wait_and_flush() { - ERR_FAIL_NULL(sync); - sync->wait(); + ERR_FAIL_COND(pump_task_id == WorkerThreadPool::INVALID_TASK_ID); + WorkerThreadPool::get_singleton()->wait_for_task_completion(pump_task_id); _flush(); } - CommandQueueMT(bool p_sync); + void set_pump_task_id(WorkerThreadPool::TaskID p_task_id) { + pump_task_id = p_task_id; + } + + CommandQueueMT(); ~CommandQueueMT(); }; diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp index 09878aaee4..fee90599e0 100644 --- a/drivers/gles3/storage/particles_storage.cpp +++ b/drivers/gles3/storage/particles_storage.cpp @@ -135,7 +135,6 @@ bool ParticlesStorage::particles_get_emitting(RID p_particles) { return false; } - ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer."); Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_NULL_V(particles, false); @@ -380,10 +379,6 @@ void ParticlesStorage::particles_request_process(RID p_particles) { } AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) { - if (RSG::threaded) { - WARN_PRINT_ONCE("Calling this function with threaded rendering enabled stalls the renderer, use with care."); - } - const Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_NULL_V(particles, AABB()); @@ -1207,7 +1202,6 @@ Dependency *ParticlesStorage::particles_get_dependency(RID p_particles) const { } bool ParticlesStorage::particles_is_inactive(RID p_particles) const { - ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer."); const Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_NULL_V(particles, false); return !particles->emitting && particles->inactive; diff --git a/main/main.cpp b/main/main.cpp index 357033b6d8..5f5118aa75 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2274,6 +2274,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph // Editor and project manager cannot run with rendering in a separate thread (they will crash on startup). rtm = OS::RENDER_THREAD_SAFE; } +#if !defined(THREADS_ENABLED) + rtm = OS::RENDER_THREAD_SAFE; +#endif OS::get_singleton()->_render_thread_mode = OS::RenderThreadMode(rtm); } @@ -2717,7 +2720,9 @@ Error Main::setup2() { } if (OS::get_singleton()->_render_thread_mode == OS::RENDER_SEPARATE_THREAD) { - WARN_PRINT("The Multi-Threaded rendering thread model is experimental, and has known issues which can lead to project crashes. Use the Single-Safe option in the project settings instead."); + WARN_PRINT("The Multi-Threaded rendering thread model is experimental. Feel free to try it since it will eventually become a stable feature.\n" + "However, bear in mind that at the moment it can lead to project crashes or instability.\n" + "So, unless you want to test the engine, use the Single-Safe option in the project settings instead."); } /* Initialize Pen Tablet Driver */ @@ -4025,11 +4030,11 @@ bool Main::iteration() { if ((!force_redraw_requested) && OS::get_singleton()->is_in_low_processor_usage_mode()) { if (RenderingServer::get_singleton()->has_changed()) { RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands - Engine::get_singleton()->frames_drawn++; + Engine::get_singleton()->increment_frames_drawn(); } } else { RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands - Engine::get_singleton()->frames_drawn++; + Engine::get_singleton()->increment_frames_drawn(); force_redraw_requested = false; } } diff --git a/servers/physics_server_2d_wrap_mt.cpp b/servers/physics_server_2d_wrap_mt.cpp index f0d31ddb7a..4548bb91cb 100644 --- a/servers/physics_server_2d_wrap_mt.cpp +++ b/servers/physics_server_2d_wrap_mt.cpp @@ -33,7 +33,7 @@ #include "core/os/os.h" void PhysicsServer2DWrapMT::thread_exit() { - exit.set(); + exit = true; } void PhysicsServer2DWrapMT::thread_step(real_t p_delta) { @@ -41,25 +41,18 @@ void PhysicsServer2DWrapMT::thread_step(real_t p_delta) { step_sem.post(); } -void PhysicsServer2DWrapMT::_thread_callback(void *_instance) { - PhysicsServer2DWrapMT *vsmt = reinterpret_cast(_instance); - - vsmt->thread_loop(); -} - void PhysicsServer2DWrapMT::thread_loop() { server_thread = Thread::get_caller_id(); physics_server_2d->init(); - exit.clear(); - step_thread_up.set(); - while (!exit.is_set()) { - // flush commands one by one, until exit is requested - command_queue.wait_and_flush(); + command_queue.set_pump_task_id(server_task_id); + while (!exit) { + WorkerThreadPool::get_singleton()->yield(); + command_queue.flush_all(); } - command_queue.flush_all(); // flush all + command_queue.flush_all(); physics_server_2d->finish(); } @@ -70,18 +63,14 @@ void PhysicsServer2DWrapMT::step(real_t p_step) { if (create_thread) { command_queue.push(this, &PhysicsServer2DWrapMT::thread_step, p_step); } else { - command_queue.flush_all(); //flush all pending from other threads + command_queue.flush_all(); // Flush all pending from other threads. physics_server_2d->step(p_step); } } void PhysicsServer2DWrapMT::sync() { if (create_thread) { - if (first_frame) { - first_frame = false; - } else { - step_sem.wait(); //must not wait if a step was not issued - } + step_sem.wait(); } physics_server_2d->sync(); } @@ -96,39 +85,34 @@ void PhysicsServer2DWrapMT::end_sync() { void PhysicsServer2DWrapMT::init() { if (create_thread) { - thread.start(_thread_callback, this); - while (!step_thread_up.is_set()) { - OS::get_singleton()->delay_usec(1000); - } + exit = false; + server_task_id = WorkerThreadPool::get_singleton()->add_task(callable_mp(this, &PhysicsServer2DWrapMT::thread_loop), true); + step_sem.post(); } else { physics_server_2d->init(); } } void PhysicsServer2DWrapMT::finish() { - if (thread.is_started()) { + if (create_thread) { command_queue.push(this, &PhysicsServer2DWrapMT::thread_exit); - thread.wait_to_finish(); + if (server_task_id != WorkerThreadPool::INVALID_TASK_ID) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(server_task_id); + server_task_id = WorkerThreadPool::INVALID_TASK_ID; + } } else { physics_server_2d->finish(); } } -PhysicsServer2DWrapMT::PhysicsServer2DWrapMT(PhysicsServer2D *p_contained, bool p_create_thread) : - command_queue(p_create_thread) { +PhysicsServer2DWrapMT::PhysicsServer2DWrapMT(PhysicsServer2D *p_contained, bool p_create_thread) { physics_server_2d = p_contained; create_thread = p_create_thread; - - if (!p_create_thread) { - server_thread = Thread::get_caller_id(); - } else { - server_thread = 0; + if (!create_thread) { + server_thread = Thread::MAIN_ID; } - - main_thread = Thread::get_caller_id(); } PhysicsServer2DWrapMT::~PhysicsServer2DWrapMT() { memdelete(physics_server_2d); - //finish(); } diff --git a/servers/physics_server_2d_wrap_mt.h b/servers/physics_server_2d_wrap_mt.h index 3bebe5df85..5e2b3b4086 100644 --- a/servers/physics_server_2d_wrap_mt.h +++ b/servers/physics_server_2d_wrap_mt.h @@ -32,6 +32,7 @@ #define PHYSICS_SERVER_2D_WRAP_MT_H #include "core/config/project_settings.h" +#include "core/object/worker_thread_pool.h" #include "core/os/thread.h" #include "core/templates/command_queue_mt.h" #include "core/templates/safe_refcount.h" @@ -43,30 +44,27 @@ #define SYNC_DEBUG #endif +#ifdef DEBUG_ENABLED +#define MAIN_THREAD_SYNC_WARN WARN_PRINT("Call to " + String(__FUNCTION__) + " causing PhysicsServer2D synchronizations on every frame. This significantly affects performance."); +#endif + class PhysicsServer2DWrapMT : public PhysicsServer2D { - mutable PhysicsServer2D *physics_server_2d; + mutable PhysicsServer2D *physics_server_2d = nullptr; mutable CommandQueueMT command_queue; - static void _thread_callback(void *_instance); void thread_loop(); - Thread::ID server_thread; - Thread::ID main_thread; - SafeFlag exit; - Thread thread; - SafeFlag step_thread_up; + Thread::ID server_thread = Thread::UNASSIGNED_ID; + WorkerThreadPool::TaskID server_task_id = WorkerThreadPool::INVALID_TASK_ID; + bool exit = false; + Semaphore step_sem; bool create_thread = false; - Semaphore step_sem; void thread_step(real_t p_delta); void thread_exit(); - bool first_frame = true; - - Mutex alloc_mutex; - public: #define ServerName PhysicsServer2D #define ServerNameWrapMT PhysicsServer2DWrapMT @@ -94,7 +92,7 @@ public: //these work well, but should be used from the main thread only bool shape_collide(RID p_shape_A, const Transform2D &p_xform_A, const Vector2 &p_motion_A, RID p_shape_B, const Transform2D &p_xform_B, const Vector2 &p_motion_B, Vector2 *r_results, int p_result_max, int &r_result_count) override { - ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false); + ERR_FAIL_COND_V(!Thread::is_main_thread(), false); return physics_server_2d->shape_collide(p_shape_A, p_xform_A, p_motion_A, p_shape_B, p_xform_B, p_motion_B, r_results, p_result_max, r_result_count); } @@ -109,18 +107,18 @@ public: // this function only works on physics process, errors and returns null otherwise PhysicsDirectSpaceState2D *space_get_direct_state(RID p_space) override { - ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), nullptr); + ERR_FAIL_COND_V(!Thread::is_main_thread(), nullptr); return physics_server_2d->space_get_direct_state(p_space); } FUNC2(space_set_debug_contacts, RID, int); virtual Vector space_get_contacts(RID p_space) const override { - ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), Vector()); + ERR_FAIL_COND_V(!Thread::is_main_thread(), Vector()); return physics_server_2d->space_get_contacts(p_space); } virtual int space_get_contact_count(RID p_space) const override { - ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), 0); + ERR_FAIL_COND_V(!Thread::is_main_thread(), 0); return physics_server_2d->space_get_contact_count(p_space); } @@ -261,13 +259,13 @@ public: FUNC2(body_set_pickable, RID, bool); bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override { - ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false); + ERR_FAIL_COND_V(!Thread::is_main_thread(), false); return physics_server_2d->body_test_motion(p_body, p_parameters, r_result); } // this function only works on physics process, errors and returns null otherwise PhysicsDirectBodyState2D *body_get_direct_state(RID p_body) override { - ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), nullptr); + ERR_FAIL_COND_V(!Thread::is_main_thread(), nullptr); return physics_server_2d->body_get_direct_state(p_body); } @@ -338,4 +336,8 @@ public: #endif #undef SYNC_DEBUG +#ifdef DEBUG_ENABLED +#undef MAIN_THREAD_SYNC_WARN +#endif + #endif // PHYSICS_SERVER_2D_WRAP_MT_H diff --git a/servers/physics_server_3d_wrap_mt.cpp b/servers/physics_server_3d_wrap_mt.cpp index 9cdd144525..f8f60281a7 100644 --- a/servers/physics_server_3d_wrap_mt.cpp +++ b/servers/physics_server_3d_wrap_mt.cpp @@ -41,22 +41,15 @@ void PhysicsServer3DWrapMT::thread_step(real_t p_delta) { step_sem.post(); } -void PhysicsServer3DWrapMT::_thread_callback(void *_instance) { - PhysicsServer3DWrapMT *vsmt = reinterpret_cast(_instance); - - vsmt->thread_loop(); -} - void PhysicsServer3DWrapMT::thread_loop() { server_thread = Thread::get_caller_id(); physics_server_3d->init(); - exit = false; - step_thread_up = true; + command_queue.set_pump_task_id(server_task_id); while (!exit) { - // flush commands one by one, until exit is requested - command_queue.wait_and_flush(); + WorkerThreadPool::get_singleton()->yield(); + command_queue.flush_all(); } command_queue.flush_all(); // flush all @@ -70,18 +63,14 @@ void PhysicsServer3DWrapMT::step(real_t p_step) { if (create_thread) { command_queue.push(this, &PhysicsServer3DWrapMT::thread_step, p_step); } else { - command_queue.flush_all(); //flush all pending from other threads + command_queue.flush_all(); // Flush all pending from other threads. physics_server_3d->step(p_step); } } void PhysicsServer3DWrapMT::sync() { if (create_thread) { - if (first_frame) { - first_frame = false; - } else { - step_sem.wait(); //must not wait if a step was not issued - } + step_sem.wait(); } physics_server_3d->sync(); } @@ -96,39 +85,34 @@ void PhysicsServer3DWrapMT::end_sync() { void PhysicsServer3DWrapMT::init() { if (create_thread) { - thread.start(_thread_callback, this); - while (!step_thread_up) { - OS::get_singleton()->delay_usec(1000); - } + exit = false; + server_task_id = WorkerThreadPool::get_singleton()->add_task(callable_mp(this, &PhysicsServer3DWrapMT::thread_loop), true); + step_sem.post(); } else { physics_server_3d->init(); } } void PhysicsServer3DWrapMT::finish() { - if (thread.is_started()) { + if (create_thread) { command_queue.push(this, &PhysicsServer3DWrapMT::thread_exit); - thread.wait_to_finish(); + if (server_task_id != WorkerThreadPool::INVALID_TASK_ID) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(server_task_id); + server_task_id = WorkerThreadPool::INVALID_TASK_ID; + } } else { physics_server_3d->finish(); } } -PhysicsServer3DWrapMT::PhysicsServer3DWrapMT(PhysicsServer3D *p_contained, bool p_create_thread) : - command_queue(p_create_thread) { +PhysicsServer3DWrapMT::PhysicsServer3DWrapMT(PhysicsServer3D *p_contained, bool p_create_thread) { physics_server_3d = p_contained; create_thread = p_create_thread; - - if (!p_create_thread) { - server_thread = Thread::get_caller_id(); - } else { - server_thread = 0; + if (!create_thread) { + server_thread = Thread::MAIN_ID; } - - main_thread = Thread::get_caller_id(); } PhysicsServer3DWrapMT::~PhysicsServer3DWrapMT() { memdelete(physics_server_3d); - //finish(); } diff --git a/servers/physics_server_3d_wrap_mt.h b/servers/physics_server_3d_wrap_mt.h index fc8930977d..22f3ee0e45 100644 --- a/servers/physics_server_3d_wrap_mt.h +++ b/servers/physics_server_3d_wrap_mt.h @@ -32,6 +32,7 @@ #define PHYSICS_SERVER_3D_WRAP_MT_H #include "core/config/project_settings.h" +#include "core/object/worker_thread_pool.h" #include "core/os/thread.h" #include "core/templates/command_queue_mt.h" #include "servers/physics_server_3d.h" @@ -42,30 +43,27 @@ #define SYNC_DEBUG #endif +#ifdef DEBUG_ENABLED +#define MAIN_THREAD_SYNC_WARN WARN_PRINT("Call to " + String(__FUNCTION__) + " causing PhysicsServer3D synchronizations on every frame. This significantly affects performance."); +#endif + class PhysicsServer3DWrapMT : public PhysicsServer3D { - mutable PhysicsServer3D *physics_server_3d; + mutable PhysicsServer3D *physics_server_3d = nullptr; mutable CommandQueueMT command_queue; - static void _thread_callback(void *_instance); void thread_loop(); - Thread::ID server_thread; - Thread::ID main_thread; - volatile bool exit = false; - Thread thread; - volatile bool step_thread_up = false; + Thread::ID server_thread = Thread::UNASSIGNED_ID; + WorkerThreadPool::TaskID server_task_id = WorkerThreadPool::INVALID_TASK_ID; + bool exit = false; + Semaphore step_sem; bool create_thread = false; - Semaphore step_sem; void thread_step(real_t p_delta); void thread_exit(); - bool first_frame = true; - - Mutex alloc_mutex; - public: #define ServerName PhysicsServer3D #define ServerNameWrapMT PhysicsServer3DWrapMT @@ -98,7 +96,7 @@ public: #if 0 //these work well, but should be used from the main thread only bool shape_collide(RID p_shape_A, const Transform &p_xform_A, const Vector3 &p_motion_A, RID p_shape_B, const Transform &p_xform_B, const Vector3 &p_motion_B, Vector3 *r_results, int p_result_max, int &r_result_count) { - ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false); + ERR_FAIL_COND_V(!Thread::is_main_thread(), false); return physics_server_3d->shape_collide(p_shape_A, p_xform_A, p_motion_A, p_shape_B, p_xform_B, p_motion_B, r_results, p_result_max, r_result_count); } #endif @@ -113,18 +111,18 @@ public: // this function only works on physics process, errors and returns null otherwise PhysicsDirectSpaceState3D *space_get_direct_state(RID p_space) override { - ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), nullptr); + ERR_FAIL_COND_V(!Thread::is_main_thread(), nullptr); return physics_server_3d->space_get_direct_state(p_space); } FUNC2(space_set_debug_contacts, RID, int); virtual Vector space_get_contacts(RID p_space) const override { - ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), Vector()); + ERR_FAIL_COND_V(!Thread::is_main_thread(), Vector()); return physics_server_3d->space_get_contacts(p_space); } virtual int space_get_contact_count(RID p_space) const override { - ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), 0); + ERR_FAIL_COND_V(!Thread::is_main_thread(), 0); return physics_server_3d->space_get_contact_count(p_space); } @@ -260,13 +258,13 @@ public: FUNC2(body_set_ray_pickable, RID, bool); bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override { - ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false); + ERR_FAIL_COND_V(!Thread::is_main_thread(), false); return physics_server_3d->body_test_motion(p_body, p_parameters, r_result); } // this function only works on physics process, errors and returns null otherwise PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) override { - ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), nullptr); + ERR_FAIL_COND_V(!Thread::is_main_thread(), nullptr); return physics_server_3d->body_get_direct_state(p_body); } @@ -411,4 +409,8 @@ public: #endif #undef SYNC_DEBUG +#ifdef DEBUG_ENABLED +#undef MAIN_THREAD_SYNC_WARN +#endif + #endif // PHYSICS_SERVER_3D_WRAP_MT_H diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index c9c7c53d04..f7b28e7a1e 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -257,7 +257,6 @@ void ParticlesStorage::particles_set_emitting(RID p_particles, bool p_emitting) } bool ParticlesStorage::particles_get_emitting(RID p_particles) { - ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer."); Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_NULL_V(particles, false); @@ -608,10 +607,6 @@ void ParticlesStorage::particles_request_process(RID p_particles) { } AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) { - if (RSG::threaded) { - WARN_PRINT_ONCE("Calling this function with threaded rendering enabled stalls the renderer, use with care."); - } - const Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_NULL_V(particles, AABB()); @@ -1642,7 +1637,6 @@ Dependency *ParticlesStorage::particles_get_dependency(RID p_particles) const { } bool ParticlesStorage::particles_is_inactive(RID p_particles) const { - ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer."); const Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_NULL_V(particles, false); return !particles->emitting && particles->inactive; diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 268b49ae80..7e5ccee0e3 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -69,9 +69,6 @@ void RenderingServerDefault::request_frame_drawn_callback(const Callable &p_call } void RenderingServerDefault::_draw(bool p_swap_buffers, double frame_step) { - //needs to be done before changes is reset to 0, to not force the editor to redraw - RS::get_singleton()->emit_signal(SNAME("frame_pre_draw")); - changes = 0; RSG::rasterizer->begin_frame(frame_step); @@ -220,16 +217,9 @@ void RenderingServerDefault::_finish() { void RenderingServerDefault::init() { if (create_thread) { - print_verbose("RenderingServerWrapMT: Creating render thread"); + print_verbose("RenderingServerWrapMT: Starting render thread"); DisplayServer::get_singleton()->release_rendering_thread(); - if (create_thread) { - thread.start(_thread_callback, this); - print_verbose("RenderingServerWrapMT: Starting render thread"); - } - while (!draw_thread_up.is_set()) { - OS::get_singleton()->delay_usec(1000); - } - print_verbose("RenderingServerWrapMT: Finished render thread"); + server_task_id = WorkerThreadPool::get_singleton()->add_task(callable_mp(this, &RenderingServerDefault::_thread_loop), true); } else { _init(); } @@ -238,8 +228,9 @@ void RenderingServerDefault::init() { void RenderingServerDefault::finish() { if (create_thread) { command_queue.push(this, &RenderingServerDefault::_thread_exit); - if (thread.is_started()) { - thread.wait_to_finish(); + if (server_task_id != WorkerThreadPool::INVALID_TASK_ID) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(server_task_id); + server_task_id = WorkerThreadPool::INVALID_TASK_ID; } } else { _finish(); @@ -337,37 +328,29 @@ Size2i RenderingServerDefault::get_maximum_viewport_size() const { } void RenderingServerDefault::_thread_exit() { - exit.set(); + exit = true; } void RenderingServerDefault::_thread_draw(bool p_swap_buffers, double frame_step) { _draw(p_swap_buffers, frame_step); } -void RenderingServerDefault::_thread_flush() { -} - -void RenderingServerDefault::_thread_callback(void *_instance) { - RenderingServerDefault *vsmt = reinterpret_cast(_instance); - - vsmt->_thread_loop(); -} - void RenderingServerDefault::_thread_loop() { server_thread = Thread::get_caller_id(); DisplayServer::get_singleton()->gl_window_make_current(DisplayServer::MAIN_WINDOW_ID); // Move GL to this thread. _init(); - draw_thread_up.set(); - while (!exit.is_set()) { - // flush commands one by one, until exit is requested - command_queue.wait_and_flush(); + command_queue.set_pump_task_id(server_task_id); + while (!exit) { + WorkerThreadPool::get_singleton()->yield(); + command_queue.flush_all(); } - command_queue.flush_all(); // flush all + command_queue.flush_all(); _finish(); + DisplayServer::get_singleton()->release_rendering_thread(); } /* INTERPOLATION */ @@ -383,15 +366,15 @@ void RenderingServerDefault::set_physics_interpolation_enabled(bool p_enabled) { /* EVENT QUEUING */ void RenderingServerDefault::sync() { - if (create_thread) { - command_queue.push_and_sync(this, &RenderingServerDefault::_thread_flush); - } else { - command_queue.flush_all(); //flush all pending from other threads + if (!create_thread) { + command_queue.flush_all(); // Flush all pending from other threads. } } void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) { ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Manually triggering the draw function from the RenderingServer can only be done on the main thread. Call this function from the main thread or use call_deferred()."); + // Needs to be done before changes is reset to 0, to not force the editor to redraw. + RS::get_singleton()->emit_signal(SNAME("frame_pre_draw")); if (create_thread) { command_queue.push(this, &RenderingServerDefault::_thread_draw, p_swap_buffers, frame_step); } else { @@ -403,21 +386,14 @@ void RenderingServerDefault::_call_on_render_thread(const Callable &p_callable) p_callable.call(); } -RenderingServerDefault::RenderingServerDefault(bool p_create_thread) : - command_queue(p_create_thread) { +RenderingServerDefault::RenderingServerDefault(bool p_create_thread) { RenderingServer::init(); -#ifdef THREADS_ENABLED create_thread = p_create_thread; if (!create_thread) { - server_thread = Thread::get_caller_id(); - } else { - server_thread = 0; + server_thread = Thread::MAIN_ID; } -#else - create_thread = false; - server_thread = Thread::get_main_id(); -#endif + RSG::threaded = create_thread; RSG::canvas = memnew(RendererCanvasCull); diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index c50472c0cd..f94323f198 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -31,6 +31,7 @@ #ifndef RENDERING_SERVER_DEFAULT_H #define RENDERING_SERVER_DEFAULT_H +#include "core/object/worker_thread_pool.h" #include "core/os/thread.h" #include "core/templates/command_queue_mt.h" #include "core/templates/hash_map.h" @@ -75,22 +76,17 @@ class RenderingServerDefault : public RenderingServer { mutable CommandQueueMT command_queue; - static void _thread_callback(void *_instance); void _thread_loop(); - Thread::ID server_thread = 0; - SafeFlag exit; - Thread thread; - SafeFlag draw_thread_up; - bool create_thread; + Thread::ID server_thread = Thread::UNASSIGNED_ID; + WorkerThreadPool::TaskID server_task_id = WorkerThreadPool::INVALID_TASK_ID; + bool exit = false; + bool create_thread = false; void _thread_draw(bool p_swap_buffers, double frame_step); - void _thread_flush(); void _thread_exit(); - Mutex alloc_mutex; - void _draw(bool p_swap_buffers, double frame_step); void _init(); void _finish(); @@ -127,6 +123,10 @@ public: #define SYNC_DEBUG #endif +#ifdef DEBUG_ENABLED +#define MAIN_THREAD_SYNC_WARN WARN_PRINT("Call to " + String(__FUNCTION__) + " causing RenderingServer synchronizations on every frame. This significantly affects performance."); +#endif + #include "servers/server_wrap_mt_common.h" /* TEXTURE API */ @@ -1013,6 +1013,9 @@ public: #undef ServerName #undef WRITE_ACTION #undef SYNC_DEBUG +#ifdef DEBUG_ENABLED +#undef MAIN_THREAD_SYNC_WARN +#endif virtual uint64_t get_rendering_info(RenderingInfo p_info) override; virtual RenderingDevice::DeviceType get_video_adapter_type() const override; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 96d317ebd3..bbe6b1ad0d 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -83,25 +83,16 @@ static PackedInt64Array to_int_array(const Vector &ids) { } PackedInt64Array RenderingServer::_instances_cull_aabb_bind(const AABB &p_aabb, RID p_scenario) const { - if (RSG::threaded) { - WARN_PRINT_ONCE("Using this function with a threaded renderer hurts performance, as it causes a server stall."); - } Vector ids = instances_cull_aabb(p_aabb, p_scenario); return to_int_array(ids); } PackedInt64Array RenderingServer::_instances_cull_ray_bind(const Vector3 &p_from, const Vector3 &p_to, RID p_scenario) const { - if (RSG::threaded) { - WARN_PRINT_ONCE("Using this function with a threaded renderer hurts performance, as it causes a server stall."); - } Vector ids = instances_cull_ray(p_from, p_to, p_scenario); return to_int_array(ids); } PackedInt64Array RenderingServer::_instances_cull_convex_bind(const TypedArray &p_convex, RID p_scenario) const { - if (RSG::threaded) { - WARN_PRINT_ONCE("Using this function with a threaded renderer hurts performance, as it causes a server stall."); - } Vector planes; for (int i = 0; i < p_convex.size(); ++i) { const Variant &v = p_convex[i]; diff --git a/servers/server_wrap_mt_common.h b/servers/server_wrap_mt_common.h index 1a73c97fc7..40867490ca 100644 --- a/servers/server_wrap_mt_common.h +++ b/servers/server_wrap_mt_common.h @@ -31,12 +31,22 @@ #ifndef SERVER_WRAP_MT_COMMON_H #define SERVER_WRAP_MT_COMMON_H +#ifdef DEBIG_ENABLED +#define MAIN_THREAD_SYNC_CHECK \ + if (unlikely(Thread::is_main_thread() && Engine::get_singleton()->notify_frame_server_synced())) { \ + MAIN_THREAD_SYNC_WARN \ + } +#else +#define MAIN_THREAD_SYNC_CHECK +#endif + #define FUNC0R(m_r, m_type) \ virtual m_r m_type() override { \ if (Thread::get_caller_id() != server_thread) { \ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -68,6 +78,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -102,6 +113,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(); \ @@ -113,6 +125,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(); \ @@ -128,6 +141,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -141,6 +155,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -154,6 +169,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1); \ @@ -165,6 +181,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1); \ @@ -199,6 +216,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -212,6 +230,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -225,6 +244,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2); \ @@ -236,6 +256,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2); \ @@ -270,6 +291,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -283,6 +305,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -296,6 +319,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2, p3); \ @@ -307,6 +331,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2, p3); \ @@ -341,6 +366,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -354,6 +380,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -367,6 +394,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2, p3, p4); \ @@ -378,6 +406,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2, p3, p4); \ @@ -412,6 +441,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -425,6 +455,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -438,6 +469,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2, p3, p4, p5); \ @@ -449,6 +481,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2, p3, p4, p5); \ @@ -483,6 +516,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -496,6 +530,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -509,6 +544,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2, p3, p4, p5, p6); \ @@ -520,6 +556,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2, p3, p4, p5, p6); \ @@ -554,6 +591,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -567,6 +605,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -580,6 +619,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2, p3, p4, p5, p6, p7); \ @@ -591,6 +631,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2, p3, p4, p5, p6, p7); \ @@ -625,6 +666,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -638,6 +680,7 @@ m_r ret; \ command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8, &ret); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ return ret; \ } else { \ command_queue.flush_if_pending(); \ @@ -651,6 +694,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2, p3, p4, p5, p6, p7, p8); \ @@ -662,6 +706,7 @@ if (Thread::get_caller_id() != server_thread) { \ command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8); \ SYNC_DEBUG \ + MAIN_THREAD_SYNC_CHECK \ } else { \ command_queue.flush_if_pending(); \ server_name->m_type(p1, p2, p3, p4, p5, p6, p7, p8); \