Multithread Support with Emscripten #110
Replies: 9 comments 28 replies
-
To enable a build with pThreads, currently you need to make sure that all 3x sub-projects are compiled with flags to enable shared memory and multi-threading. The project builds a jolt-static library, so that needs it
The project also builds a glue.o, and then finally the wasm module again
And set the environment to run in a worker:
You will need to copy the /dist/*.worker.js file to your normal JS folder so that the main thread can spawn the child-threads Note: Chrome is fine with loading this on localhost as long as you have COOP/COEP enabled The biggest blocker on enabling threads so far is Safari has problems with the SharedMemory access threads. This causes problems where suddenly collision stops working all together, so usually after running with pThread enabled, you will just have bodies stop responding to collision detection and fall through each other. I run the test every so often, and have been looking at new Safari Preview release notes Non-Preview (existing Safari, iPhone) - Safari 17.2.1, iPhone iOS 17.3.1
Safari Preview - Release 188 (Safari 17.4) - requires HTTPS even on localhost + COEP/COOP:
The usage of custom threads and pools is because of the way that Javascript interfaces with the Jolts internal event system. To some degree it was also me seeing if I could hack my way around the Safari bug, but also was to give myself extra hooks into the Thread Spawn logic. Even if it does become stable, you'd want to ensure that you actually get a benefit from running the physics in more than 1x thread. Even with single-threading the physics, you can still setup to use an external SharedMem blob to syncs Position/Rotations and run [UI Main Thread, 3D Render Thread, PhysicsThread] without multi-threading the Physics Library |
Beta Was this translation helpful? Give feedback.
-
Thank you for the super in-depth response! I appreciate your insights and all the work you've clearly done on this path. Regarding your main blocker, running in safari:
Is this only an issue on Mobile safari or is the desktop version showing similar issues? Have other platforms/orgs discussed similar issues with Obviously, I'd like to see everything working perfectly on every platform, but if Mobile Safari is the only platform with such issues then I think it not a bad idea to move forward with a working threaded version. We could disclaimer the limitation for Safari continue to test/work towards safari working properly. If we had a multi-thread version we could build examples for the different usecases/setups and deal with other issues like the ones you mentioned in your second reply. Once safari multi-threading became stable they could use the same examples/docs for their setup...
Totally agree. For me, it's adding/removing bodies to the system as well as syncing position data that would be most advantageous. Also having a single point to listen to contact/body events without having to pipe events through |
Beta Was this translation helpful? Give feedback.
-
I didn’t mean to distract with my to keep this thread on track, what is your test setup? I can run it against safari latest on desktop and mobile and see if I can replicate the results. also I like the idea of multi-thread being a build target, what would the steps be to make that happen? |
Beta Was this translation helpful? Give feedback.
-
I think I have an idea why this happens, I had to increment the thread pool size and match it with the max threads that the job scheduler will take or else I got hangs in Chrome as well. Maybe you can try again with the new code and see if it is fixed now? Anyway the change is now merged and when enabled it gives a very good speed boost (the stress test runs 15fps without it and 60fps with it on my machine). B.t.w. I tried making a web server that enables COOP/COEP but this is googled together, let me know if I did it totally wrong (especially how you're supposed to work with packages is a mystery to me). |
Beta Was this translation helpful? Give feedback.
-
A perk of the PTHREAD_POOL_SIZE logic is that it will pre-init all the threads, then pause until you actually create the physicsSystem. This gives both the ability to fully start up the thread and Jolt, and you almost have a solid barrier where you can configure the worker threads. Taking that into account, when you do a multi-threaded JS run of Jolt, you can pass a custom script that will only run on the workers in the joltInit This means that we have a solid block where between when threads are created ( initJolt) and when they are run (new JoltInterface). The kind-of-zero communication logic that you can use (and I have in the example below) is that you can overwrite the WebIDL cache with a JS Proxy, meaning that any arbitrary request for a C++ Pointer will return whatever JS Interface code you want every time. This technique uses mangled names if the library is distro-mode due to minification, but there are ways around that to prevent them from being minified away, using the -post-js emscripten compiled parameter. If this proxy concept wasn't so hacky, that could be handled with a custom setClassProxy method to the Wasm Module Interface. I'm still not solid on a good way to pass data worker thread, mainly pointers, with emscripten's internal scoping of everything Worker related. I was tempted to use BroadcastChannel, but there's not a good way to barrier until all 16 workers are running then send the data and then barrier until they are done. I'd tried working through LocalStorage, BroadcastChannel, IndexedDB ... none are exactly clean, and the solid cleanest way is recoding the Worker JS to add a new Note: The code on this branch is * not * the way you'd want to actually do this approach, I just wanted a temp branch to show a way that gets you 80% of the way there. Even if you did plan to just proxy all JS Listeners of a specific class, you'd probably still want to an actual new JS file instead of dynamic JS-blob of text. |
Beta Was this translation helpful? Give feedback.
-
I am not sure if it is of help, but want to share my setup, where I run Jolt in a webworker. I do allow JS callbacks, but without going to main thread. The main concept is that on main thread I stringify the JS function that is supposed to become a callback, send to worker, and evaluate it to turn it back into an executable JS function. // main thread
function onContactAdded(b1Pointer, b2Pointer, manifoldPointer, settingsPointer) {
// custom code
}
const overrides = {
onContactAdded: new String(onContactAdded),
onContactValidate: ...
};
// send overrides to worker // worker
listener.OnContactAdded = eval('(' + funcStr + ')').bind(this); This way the worker can have a custom JS callback, but without a need to talk to main thread. This is where the User Data fields become useful on the objects. Since these callback have no access to main thread or the app scope, they can use User Data fields to toggle a branch, e.g.: function onContactSolve(character, ...) {
...
const allowSliding = character.GetShape().GetUserData() === 1 ? true : false;
if (!allowSliding && ...) {
...
}
} |
Beta Was this translation helpful? Give feedback.
-
iOS 17.4 now appears to be public. This has the Safari fix that allows for multi-threading, so multithreading is theoretically now available on iPhones. I've updated and confirmed the 16-thread-pool build (enable_multi_threading) works on my phone. The challenge would be how to filter so that iOS 17.3 devices do not attempt to load the multithread builds and fail. ++ Edit: (note, these don't call out anything WASM-Multhreading-Related) |
Beta Was this translation helpful? Give feedback.
-
If example updates are desired, I'm not sure how we would want to update the examples to support this new build variant I believe the required update is very minor for most cases, mainly just making sure anything important tags the Bodies with a UserData, but I do not know if it would be confusing to have it mixed in the middle of single-threaded versions. In the best cases, it is just having an optional worker-loader JS file, and you can blindly pass that URL to the initJolt() call and it will never be used unless the code kicks off workers. The three most complicated ones are:
|
Beta Was this translation helpful? Give feedback.
-
I'm having a little bit of a problem following at the end here, but it seems like we're trying to solve many problems at once. In the future the library could be responsible for deciding which version of the platform to use but for now leave it to the end user. I think that means we need a basic build like @jrouwe mentions Right now, I have to build my own communication api/channels and run Jolt on a separate worker to not have it block the rendering thread. How do we build a simple (understandably limited) version of the Falling Shapes demo that does that? How can we demonstrate the benefits of being multi-threaded? crank the shapes up to 1,000? Is that possible? |
Beta Was this translation helpful? Give feedback.
-
I saw the other discussion HERE but that person is trying to implement their own pooling/situation without the use of Pthreads.
As Emscripten supports pThreads and C++ Threads are fairly common in WebAssebly I'm wondering what it would take to get this functionality in Jolt.
From my limited understanding,
std::threads
is built on-top ofpThreads
so from other examples I've seen should compile with Emscripten without too much hassle. There are limitations and things that might have to be done but I don't think it as big of a task as that other user.From the feature table Threads are well supported in all major browsers, so this looks like a doable thing.
One thing to note, is regardless of thread support with Jolt, Web users would likely still be required to use workers to access things as the point with Jolt is we can access them INSIDE other threads. But this way at least, we would push a lot of the body/object tracking back into jolt vs doing it all in duplicate.
I haven't seen anything in the jolt docs that would let you get like 4 threads going for collision detection vs 1, but that's probably due to my lack of C++ skills.
Beta Was this translation helpful? Give feedback.
All reactions