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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multithreading support (reality tabs) #760

Open
wants to merge 372 commits into
base: master
from

Conversation

Projects
None yet
2 participants
@modulesio
Copy link
Member

modulesio 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. 馃 馃寛

@modulesio

This comment has been minimized.

Copy link
Member Author

modulesio commented Jan 24, 2019

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

@modulesio modulesio force-pushed the user-parallel branch from 8c8ba92 to 3389f3c Jan 25, 2019

@modulesio modulesio force-pushed the window-renderloop branch from 58daaee to f7e3485 Jan 26, 2019

@modulesio modulesio force-pushed the user-parallel branch from 3389f3c to 392014d Jan 26, 2019

@modulesio modulesio force-pushed the window-renderloop branch from 95d1014 to 85b1f36 Jan 27, 2019

@modulesio modulesio force-pushed the user-parallel branch from 392014d to 9d75317 Jan 27, 2019

@modulesio modulesio changed the base branch from window-renderloop to master Jan 29, 2019

@modulesio modulesio force-pushed the user-parallel branch from 0eb78f8 to 17b1258 Feb 4, 2019

@modulesio modulesio force-pushed the user-parallel branch 2 times, most recently from 7566c9b to 5deadb3 Feb 12, 2019

@modulesio

This comment has been minimized.

Copy link
Member Author

modulesio commented Feb 15, 2019

This should be building now.

@modulesio modulesio force-pushed the user-parallel branch from ad5ff71 to f045f21 Feb 16, 2019

@modulesio modulesio force-pushed the user-parallel branch 7 times, most recently from afe2d2f to 2d9ea63 Mar 7, 2019

@chrisplatorres

This comment has been minimized.

Copy link
Member

chrisplatorres commented Mar 14, 2019

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

@modulesio modulesio force-pushed the user-parallel branch 6 times, most recently from d5ae9f3 to d2216dd Mar 24, 2019

@modulesio modulesio force-pushed the user-parallel branch from beb1fab to de3c3f6 Apr 16, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can鈥檛 perform that action at this time.