Skip to content

Commit 65686de

Browse files
reduzRandomShaper
andcommitted
Use WorkerThreadPool for Server threads
* Servers now use WorkerThreadPool for background computation. * This helps keep the number of threads used fixed at all times. * It also ensures everything works on HTML5 with threads. * And makes it easier to support disabling threads for also HTML5. CommandQueueMT now syncs with the servers via the WorkerThreadPool yielding mechanism, which makes its classic main sync semaphore superfluous. Also, some warnings about calls that kill performance when using threaded rendering are removed because there's a mechanism that warns about that in a more general fashion. Co-authored-by: Pedro J. Estébanez <[email protected]>
1 parent c28f590 commit 65686de

15 files changed

+208
-204
lines changed

core/config/engine.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ int Engine::get_audio_output_latency() const {
8282
return _audio_output_latency;
8383
}
8484

85+
void Engine::increment_frames_drawn() {
86+
if (frame_server_synced) {
87+
server_syncs++;
88+
} else {
89+
server_syncs = 0;
90+
}
91+
frame_server_synced = false;
92+
93+
frames_drawn++;
94+
}
95+
8596
uint64_t Engine::get_frames_drawn() {
8697
return frames_drawn;
8798
}
@@ -364,6 +375,11 @@ Engine *Engine::get_singleton() {
364375
return singleton;
365376
}
366377

378+
bool Engine::notify_frame_server_synced() {
379+
frame_server_synced = true;
380+
return server_syncs > SERVER_SYNC_FRAME_COUNT_WARNING;
381+
}
382+
367383
Engine::Engine() {
368384
singleton = this;
369385
}

core/config/engine.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ class Engine {
9191
String write_movie_path;
9292
String shader_cache_path;
9393

94+
static constexpr int SERVER_SYNC_FRAME_COUNT_WARNING = 5;
95+
int server_syncs = 0;
96+
bool frame_server_synced = false;
97+
9498
public:
9599
static Engine *get_singleton();
96100

@@ -179,6 +183,9 @@ class Engine {
179183
bool is_generate_spirv_debug_info_enabled() const;
180184
int32_t get_gpu_index() const;
181185

186+
void increment_frames_drawn();
187+
bool notify_frame_server_synced();
188+
182189
Engine();
183190
virtual ~Engine() {}
184191
};

core/templates/command_queue_mt.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,8 @@ CommandQueueMT::SyncSemaphore *CommandQueueMT::_alloc_sync_sem() {
7070
return &sync_sems[idx];
7171
}
7272

73-
CommandQueueMT::CommandQueueMT(bool p_sync) {
74-
if (p_sync) {
75-
sync = memnew(Semaphore);
76-
}
73+
CommandQueueMT::CommandQueueMT() {
7774
}
7875

7976
CommandQueueMT::~CommandQueueMT() {
80-
if (sync) {
81-
memdelete(sync);
82-
}
8377
}

core/templates/command_queue_mt.h

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -248,16 +248,17 @@
248248
#define CMD_TYPE(N) Command##N<T, M COMMA(N) COMMA_SEP_LIST(TYPE_ARG, N)>
249249
#define CMD_ASSIGN_PARAM(N) cmd->p##N = p##N
250250

251-
#define DECL_PUSH(N) \
252-
template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \
253-
void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \
254-
CMD_TYPE(N) *cmd = allocate_and_lock<CMD_TYPE(N)>(); \
255-
cmd->instance = p_instance; \
256-
cmd->method = p_method; \
257-
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
258-
unlock(); \
259-
if (sync) \
260-
sync->post(); \
251+
#define DECL_PUSH(N) \
252+
template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \
253+
void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \
254+
CMD_TYPE(N) *cmd = allocate_and_lock<CMD_TYPE(N)>(); \
255+
cmd->instance = p_instance; \
256+
cmd->method = p_method; \
257+
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
258+
if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
259+
WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
260+
} \
261+
unlock(); \
261262
}
262263

263264
#define CMD_RET_TYPE(N) CommandRet##N<T, M, COMMA_SEP_LIST(TYPE_ARG, N) COMMA(N) R>
@@ -272,9 +273,10 @@
272273
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
273274
cmd->ret = r_ret; \
274275
cmd->sync_sem = ss; \
276+
if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
277+
WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
278+
} \
275279
unlock(); \
276-
if (sync) \
277-
sync->post(); \
278280
ss->sem.wait(); \
279281
ss->in_use = false; \
280282
}
@@ -290,9 +292,10 @@
290292
cmd->method = p_method; \
291293
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
292294
cmd->sync_sem = ss; \
295+
if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
296+
WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
297+
} \
293298
unlock(); \
294-
if (sync) \
295-
sync->post(); \
296299
ss->sem.wait(); \
297300
ss->in_use = false; \
298301
}
@@ -340,7 +343,7 @@ class CommandQueueMT {
340343
LocalVector<uint8_t> command_mem;
341344
SyncSemaphore sync_sems[SYNC_SEMAPHORES];
342345
Mutex mutex;
343-
Semaphore *sync = nullptr;
346+
WorkerThreadPool::TaskID pump_task_id = WorkerThreadPool::INVALID_TASK_ID;
344347
uint64_t flush_read_ptr = 0;
345348

346349
template <typename T>
@@ -421,12 +424,16 @@ class CommandQueueMT {
421424
}
422425

423426
void wait_and_flush() {
424-
ERR_FAIL_NULL(sync);
425-
sync->wait();
427+
ERR_FAIL_COND(pump_task_id == WorkerThreadPool::INVALID_TASK_ID);
428+
WorkerThreadPool::get_singleton()->wait_for_task_completion(pump_task_id);
426429
_flush();
427430
}
428431

429-
CommandQueueMT(bool p_sync);
432+
void set_pump_task_id(WorkerThreadPool::TaskID p_task_id) {
433+
pump_task_id = p_task_id;
434+
}
435+
436+
CommandQueueMT();
430437
~CommandQueueMT();
431438
};
432439

drivers/gles3/storage/particles_storage.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,6 @@ bool ParticlesStorage::particles_get_emitting(RID p_particles) {
135135
return false;
136136
}
137137

138-
ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer.");
139138
Particles *particles = particles_owner.get_or_null(p_particles);
140139
ERR_FAIL_NULL_V(particles, false);
141140

@@ -380,10 +379,6 @@ void ParticlesStorage::particles_request_process(RID p_particles) {
380379
}
381380

382381
AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) {
383-
if (RSG::threaded) {
384-
WARN_PRINT_ONCE("Calling this function with threaded rendering enabled stalls the renderer, use with care.");
385-
}
386-
387382
const Particles *particles = particles_owner.get_or_null(p_particles);
388383
ERR_FAIL_NULL_V(particles, AABB());
389384

@@ -1207,7 +1202,6 @@ Dependency *ParticlesStorage::particles_get_dependency(RID p_particles) const {
12071202
}
12081203

12091204
bool ParticlesStorage::particles_is_inactive(RID p_particles) const {
1210-
ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer.");
12111205
const Particles *particles = particles_owner.get_or_null(p_particles);
12121206
ERR_FAIL_NULL_V(particles, false);
12131207
return !particles->emitting && particles->inactive;

main/main.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2274,6 +2274,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
22742274
// Editor and project manager cannot run with rendering in a separate thread (they will crash on startup).
22752275
rtm = OS::RENDER_THREAD_SAFE;
22762276
}
2277+
#if !defined(THREADS_ENABLED)
2278+
rtm = OS::RENDER_THREAD_SAFE;
2279+
#endif
22772280
OS::get_singleton()->_render_thread_mode = OS::RenderThreadMode(rtm);
22782281
}
22792282

@@ -2717,7 +2720,9 @@ Error Main::setup2() {
27172720
}
27182721

27192722
if (OS::get_singleton()->_render_thread_mode == OS::RENDER_SEPARATE_THREAD) {
2720-
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.");
2723+
WARN_PRINT("The Multi-Threaded rendering thread model is experimental. Feel free to try it since it will eventually become a stable feature.\n"
2724+
"However, bear in mind that at the moment it can lead to project crashes or instability.\n"
2725+
"So, unless you want to test the engine, use the Single-Safe option in the project settings instead.");
27212726
}
27222727

27232728
/* Initialize Pen Tablet Driver */
@@ -4025,11 +4030,11 @@ bool Main::iteration() {
40254030
if ((!force_redraw_requested) && OS::get_singleton()->is_in_low_processor_usage_mode()) {
40264031
if (RenderingServer::get_singleton()->has_changed()) {
40274032
RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands
4028-
Engine::get_singleton()->frames_drawn++;
4033+
Engine::get_singleton()->increment_frames_drawn();
40294034
}
40304035
} else {
40314036
RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands
4032-
Engine::get_singleton()->frames_drawn++;
4037+
Engine::get_singleton()->increment_frames_drawn();
40334038
force_redraw_requested = false;
40344039
}
40354040
}

servers/physics_server_2d_wrap_mt.cpp

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -33,33 +33,26 @@
3333
#include "core/os/os.h"
3434

3535
void PhysicsServer2DWrapMT::thread_exit() {
36-
exit.set();
36+
exit = true;
3737
}
3838

3939
void PhysicsServer2DWrapMT::thread_step(real_t p_delta) {
4040
physics_server_2d->step(p_delta);
4141
step_sem.post();
4242
}
4343

44-
void PhysicsServer2DWrapMT::_thread_callback(void *_instance) {
45-
PhysicsServer2DWrapMT *vsmt = reinterpret_cast<PhysicsServer2DWrapMT *>(_instance);
46-
47-
vsmt->thread_loop();
48-
}
49-
5044
void PhysicsServer2DWrapMT::thread_loop() {
5145
server_thread = Thread::get_caller_id();
5246

5347
physics_server_2d->init();
5448

55-
exit.clear();
56-
step_thread_up.set();
57-
while (!exit.is_set()) {
58-
// flush commands one by one, until exit is requested
59-
command_queue.wait_and_flush();
49+
command_queue.set_pump_task_id(server_task_id);
50+
while (!exit) {
51+
WorkerThreadPool::get_singleton()->yield();
52+
command_queue.flush_all();
6053
}
6154

62-
command_queue.flush_all(); // flush all
55+
command_queue.flush_all();
6356

6457
physics_server_2d->finish();
6558
}
@@ -70,18 +63,14 @@ void PhysicsServer2DWrapMT::step(real_t p_step) {
7063
if (create_thread) {
7164
command_queue.push(this, &PhysicsServer2DWrapMT::thread_step, p_step);
7265
} else {
73-
command_queue.flush_all(); //flush all pending from other threads
66+
command_queue.flush_all(); // Flush all pending from other threads.
7467
physics_server_2d->step(p_step);
7568
}
7669
}
7770

7871
void PhysicsServer2DWrapMT::sync() {
7972
if (create_thread) {
80-
if (first_frame) {
81-
first_frame = false;
82-
} else {
83-
step_sem.wait(); //must not wait if a step was not issued
84-
}
73+
step_sem.wait();
8574
}
8675
physics_server_2d->sync();
8776
}
@@ -96,39 +85,34 @@ void PhysicsServer2DWrapMT::end_sync() {
9685

9786
void PhysicsServer2DWrapMT::init() {
9887
if (create_thread) {
99-
thread.start(_thread_callback, this);
100-
while (!step_thread_up.is_set()) {
101-
OS::get_singleton()->delay_usec(1000);
102-
}
88+
exit = false;
89+
server_task_id = WorkerThreadPool::get_singleton()->add_task(callable_mp(this, &PhysicsServer2DWrapMT::thread_loop), true);
90+
step_sem.post();
10391
} else {
10492
physics_server_2d->init();
10593
}
10694
}
10795

10896
void PhysicsServer2DWrapMT::finish() {
109-
if (thread.is_started()) {
97+
if (create_thread) {
11098
command_queue.push(this, &PhysicsServer2DWrapMT::thread_exit);
111-
thread.wait_to_finish();
99+
if (server_task_id != WorkerThreadPool::INVALID_TASK_ID) {
100+
WorkerThreadPool::get_singleton()->wait_for_task_completion(server_task_id);
101+
server_task_id = WorkerThreadPool::INVALID_TASK_ID;
102+
}
112103
} else {
113104
physics_server_2d->finish();
114105
}
115106
}
116107

117-
PhysicsServer2DWrapMT::PhysicsServer2DWrapMT(PhysicsServer2D *p_contained, bool p_create_thread) :
118-
command_queue(p_create_thread) {
108+
PhysicsServer2DWrapMT::PhysicsServer2DWrapMT(PhysicsServer2D *p_contained, bool p_create_thread) {
119109
physics_server_2d = p_contained;
120110
create_thread = p_create_thread;
121-
122-
if (!p_create_thread) {
123-
server_thread = Thread::get_caller_id();
124-
} else {
125-
server_thread = 0;
111+
if (!create_thread) {
112+
server_thread = Thread::MAIN_ID;
126113
}
127-
128-
main_thread = Thread::get_caller_id();
129114
}
130115

131116
PhysicsServer2DWrapMT::~PhysicsServer2DWrapMT() {
132117
memdelete(physics_server_2d);
133-
//finish();
134118
}

0 commit comments

Comments
 (0)