4.1 KiB
RFC: Off-Thread (aka Offscreen) Rendering
- Authors: Xiaoji
- Date: March 2018
- Status: Pending Review
Notes:
Motivation
WebGL rendering performance can greatly affect UI responsiveness. The experimental OffscreenCanvas API in Chrome and Firefox allows us to move WebGL rendering to a worker thread, and free up the main thread to handle DOM interactions. It is highly desirable for heavy front-end applications.
State of the API
OffscreenCanvas is in active development (behind experimental flags) in both FF and Chrome. In Chrome, go to chrome://flags and enable "Experimental web platform features."
The API still seems limited/unstable:
- FF:
- [57.0] Only supports OffscreenCanvas with WebGL1 context;
gl.committhrows error after first render - [59.0] Crashes on
gl.commit
- [57.0] Only supports OffscreenCanvas with WebGL1 context;
- Chrome
- [64.0] Instanced rendering does not update render buffer
- [65.0] Instanced rendering works, slows down after running a while. Stops updating when set pickingPosition to
null.
Proposal: OffscreenAnimationLoop
Class name TBD. Other suggestions:
AnimationLoopProxy,OffThreadAnimationLoop,WorkerThreadAnimationLoop,ThreadedAnimationLoop,WorkerAnimationLoop
Add an OffscreenAnimationLoop that mirrors the AnimationLoop class but delegate rendering to a worker.
Static methods:
createWorker(opts)- returns script that can be used to spin up a new Worker.optsare the options used to construct anAnimationLoopinstance on the worker thread.
Constructor:
new OffscreenAnimationLoop(opts)-optsare the same options used to construct anAnimationLoop, with additional fields:opts.worker(Worker) - the worker instance running a script created byOffscreenAnimationLoop.createWorker
Methods:
setProps(props)- equivalent toanimationLoop.setProps(props)start(opts)- equivalent toanimationLoop.start(opts)stop()- equivalent toanimationLoop.stop()
Examples
Render on main thread:
/// app.js
import animationLoopOptions from './animation';
import {AnimationLoop} from 'luma.gl';
const animationLoop = new AnimationLoop({
// animationLoopOptions
});
animationLoop.start({canvas: 'lumagl-canvas'});
Render on worker thread:
/// app.js
import animationLoopOptions from './animation';
import {OffscreenAnimationLoop} from 'luma.gl';
import webworkify from 'webworkify';
const worker = OffscreenAnimationLoop.createWorker({
// animationLoopOptions
});
const animationLoop = new OffscreenAnimationLoop({
worker: webworkify(worker)
});
animationLoop.start({canvas: 'lumagl-canvas'});
Proposal: Interactivity
AnimationLoop does not directly handle user input (mousemove, click, etc.). This is usually not an issue as the user can update their own application state from event callbacks and then access that state from within onRender. In the offscreen rendering case, this is no longer possible as onRender is on a different thread from where the event callbacks are executed.
The proposed solution is to expose a new API on both AnimationLoop and OffscreenAnimationLoop for applications to send custom data to the renderer:
- Add
setUserDatamethod toAnimationLoop. It adds a fielduserDatatoanimationLoop._callbackDatawhich can be accessed in theonRendercallback. - Add
setUserDatamethod toOffscreenAnimationLoop. It posts the user data to the worker which then callsanimationLoop.setUserData.
Challenges
The main challenge in this effort is that DOM properties are accessed in various places of the code base (e.g. webgl-utils, AnimationLoop, hover/click handling, viewport controls). Certain globals such as window document and HTMLCanvasElement are not available to the worker thread. We should either improve the compatibility of existing code, or invest in separating DOM-dependent code from the core.