cadaver wrote:Urho does the same as Unity which allows to put script / logic hooks to physics update substeps (FixedUpdate), which makes it rather hard to implement physics in a separate thread. If we disallowed that, then it could be threaded, provided there's added synchronization, but again it's a performance <-> usability tradeoff.
That hooking vs. threaded physics and AI option could be switched with a CMake option and defines for 0 runtime cost and proper compiler errors (non existing functions). Or those functions are actually called in separate threads... I still would suggest an optional option as one has to pay more attention when working asynchronously.
cadaver wrote:When you program in C++ you can go crazy with your own background-threaded algorithms
I've also been thinking about what options there are.
Someone on the IRC posted this technique: http://www.panda3d.org/blog/triple-your-frame-rate/
I've seen that technique before somewhere else, it splits the independent rendering steps into different threads to have three "slices".
Could that be done with Urho as well?
About other ways to multi thread stuff: I came up with two possible ways.
One solution is the job scheduling that you mentioned the Unreal engine uses. Such a job queue sounds rather complicated and like having a real overhead.
The other possible solution I came up with is to partly synchronize threads with mutexes and/or conditional variables. All the stuff that can only be done at a specific moment (when no one else is modifying the scene for example) is done in serial and not parallel. I would have to look into mutexes and conditional variables again but it's kinda like this:
- Code: Select all
void NonMainThread() // multiple threads do this, like for AI, physics or whatever
// plan all operations like pathfinding and the actions to do
MutexGuard mg=MutextMainThread.GetMutexGuard(); // wait until the MainThread is ready and lock a mutex via a RAII mutex guard
// execute all planned actions in this serial part (AKA "synchronized part")
} // the mutex guard is destroyed and automatically unlocks the mutex to get back into parallel mode
void Update() // main thread
MutexMainThread.unlock(); // let every waiting "worker thread" do his planned actions in serial. Every worker thread should
// do only one "set" of actions (not unlock, replan and lock again -> possible deadlock).
MutexMainThread.lock(); // go back into parallel mode
I assume the non-main threads can do stuff like scene manipulations if it is synchronized like that with the main thread? Or is there some weird thread ownership like Qt has?
The second method is pretty manual and avoids a possible giant and slow job queue. Also it is more flexible as it can do everything and doesn't have to rely on special actions queued (depending on the implementation).
All variants I can think of are not that easy/"idiot safe" as a scene change (or another non-thread-safe action) may still be tried in the parallelized part and not the serial part where it is actually safe.
EDIT: for those interested, I looked a bit more in detail how UE4 does things:
This interesting "problem" occupied me as well since the multi threading idea came up. Had a partly written reply lying here since then and kept thinking about it.
Is it safe to read from the scene (like node positions or doing raytraces) when the main thread is rendering? The parallel mode has to be entered for every non-tread-safe operation of course. Usually most machines do also have atomic operations where expensive mutexes can be avoided but I doubt we can really use that as most stuff requires multiple operation without someone getting inbetween. BTW: on x86 every read is atomic even without using the special atomic instructions (manually or via C++11 std::atomic).
Edit: I think the synchronizing has to be done with a mutex for every thread that is unlocked by the main thread when it can do one "set". Has been quite a while since writing my last thread pool or something similar. But unconditional variables had been also required for something in that direction...
Edit2: Oh! I thing the conditional variables are required when one doesn't want to lock the main thread and just lock or unlock worker threads. Which is not the case here so it should work with simple mutexes. If I'm not mistaken.