Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multithreading (reality layers) #760

Merged
merged 658 commits into from
May 21, 2019
Merged

Multithreading (reality layers) #760

merged 658 commits into from
May 21, 2019

Conversation

avaer
Copy link
Member

@avaer avaer commented Jan 24, 2019

Exokit's architecture has up until this point been mostly singlethreaded. This means that the render loop, and all user code animation frames ran in the same thread. This is mostly because that is how node (used to) work.

However, this is problematic for several reasons:

  • Web APIs are architected around the idea that user code runs alone in its own thread -- for example, network requests, image decode, web workers, service workers, audio service threads all expect to be run in parallel to user code, not in the user thread
  • User threads can hold up the processing of network requests, resulting in needlessly reduced network throughput when doing heavy user work such as rendering
  • User thread lockups can lock up Exokit engine entirely
  • User contexts are very hard to track and destroy without encapsulating them inside of a true isolated V8 context environment
  • Bugs from one context can leak to other contexts
  • When Exokit loads and composes multiple WebXR iframes into a single world view ("reality tabs"), we need to make sure slow apps do not hold up fast apps -- this implies a desynchronized render loop with reprojection for apps that can't keep up, and that's only possible if we run each app in its own thread separate from the top level render loop

Node 11+12 have full threading support, which mostly follows the WebWorker API.

This PR leverages worker_threads to run all user window code in parallel, separate from the render loop. The architecture is as follows:

  • These is still a top level render loop, which has a list of top-level windows, corresponding to web window contexts
  • The render loop asynchronously dispatches the XR three-phase events to the window contexts: waitPose, tickAnimationFrame, submitFrame. This is effected by posting messages down child windows, and capturing the response in a Promise which we can .then on
  • Since each window can have child 3D <iframe>s which it is composing into its scene, each window is also its own render loop, and proxies the three-phase XR to its child windows. This forms a virtual render tree structure made of window contexts
  • Use GLSync objects from the child window renders, waited on by its owning window, in order to effect perfect frame synchronization for apps in the ideal case
  • If an app does not meet the frame deadline, we setTimeout and continue rendering without it, and do not dispatch any more requestAnimationFrame to it -- whenever the app finishes the render and resolves the corresponding Promise, we will pick up the frame on the next available render loop tick and resume further requestAnimationFrame to it
  • Everything else works normally as if we had multiple V8/GC contexts, which is friendly to OS threading, scheduling and allocation

Though this PR does not do it yet, this opens up the path to full Service Worker and audio thread support, as well as 3D framebuffer reprojection support for applications so that we maintain frame rate even in cases where the user code cannot keep up (due to loading, being badly written, etc).

Most of the changes here are instituting the new render loop request/response, and fixing the places in the code that assumed everything was running synchronously in one thread.

Overall user code should see no change here, other than that everything becomes faster for free, especially -- but not only -- when using reality tabs in XR. 🦄 🌈

@avaer
Copy link
Member Author

avaer commented Jan 24, 2019

Note this is currently based on window-renderloop, not master.

@avaer
Copy link
Member Author

avaer commented Feb 15, 2019

This should be building now.

@chrislatorres
Copy link
Contributor

Tested each example as reality tab on new user-parallel mpk:


Works as a reality tab:

paint_ml
tutorial
exobot
minimap "works", but don't see anything?
radar
microphone 

Blank as a reality tab, no errors or hard crash:

hello_ml
meshing_ml
planes_ml
graffiti_ml
hands_ml
bow_ml
pathfinding_ml
imagetracking_ml

Crashes with the following error:

FATAL ERROR: v8::HandleScope::CreateHandle() Cannot create a handle without a HandleScope

avatar_ml
shooter_ml , when pull trigger

@avaer avaer mentioned this pull request Mar 21, 2019
@avaer avaer force-pushed the user-parallel branch 6 times, most recently from d5ae9f3 to d2216dd Compare March 26, 2019 02:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants