Re: exposing CANVAS or something like it to Web Workers

On Wed, May 16, 2012 at 2:30 PM, Gregg Tavares (勤) <gman@google.com> wrote:

> Some Ideas
>
> **) Create context in main page, pass to worker*
>

Transferring an active context from one thread to another (at the
implementation level) can be hard.  It's much simpler to only create new
the contexts in the thread they'll be used, never allowing contexts to be
moved from thread to thread.

Here's a first-pass approach:

1: Add HTMLCanvasElement.getBackbuffer, which returns a lightweight
Backbuffer interface associated with the HTMLCanvasElement.  This has only
one method, getContext, which is equivalent to canvas.getContext.  (This is
exactly like the suggestion for making images available in threads.)
2: Backbuffer can be transferred from one thread to another, using the
transfer mechanism.  On transfer, any WebGL contexts (and other context
types) in the thread associated with the same backbuffer are destroyed
(putting exactly what "destroyed" means aside for later).  Additionally,
when a Backbuffer is transferred, the underlying backbuffer of the
HTMLCanvasElement is considered owned by the new thread.  All instances of
Backbuffer for that HTMLCanvasElement, including newly-created ones, are
neutered unless the backbuffer is transferred back to the UI thread.
3: As long as the backbuffer of an HTMLCanvasElement is owned by a thread
other than the UI thread, calls to the following methods on the associated
HTMLCanvasElement raise an exception: getBackbuffer, getContext,
toBlob, toDataURL.
This takes effect synchronously, as soon as transfer happens (right when
you postMessage the Backbuffer somewhere else).
4: When a Worker receives a Backbuffer, it can call Backbuffer.getContext
to create a WebGLContext (with exactly the same rules as
HTMLCanvasElement.getContext).  The results of rendering to that context
are visible in the associated HTMLCanvasElement in the main thread.

This ensures that only one thread can ever have an open context for any
backbuffer, and that pixel readback functions (toBlob and toDataURL) can't
expose the asynchronous nature of what's going on.  The transfer mechanics
are rough and need refining.

For offscreen rendering in a thread, just allow constructing Backbuffer;
for example,

var backbuffer = new Backbuffer(1024, 768);
var ctx = backbuffer.getContext("webgl");
backbuffer.resize(1920, 1200);

Backbuffer.resize is equivalent to resizing a Canvas, and only available
for Backbuffers you create yourself, not for ones created via
HTMLCanvasElement.getBackBuffer.

One problem that I havn't attempted to solve here: when the
HTMLCanvasElement is resized in the UI thread, it results in changes to
drawingBufferWidth/drawingBufferHeight.  This would expose asynchronous
behavior.  Instead, it would probably need to apply the change (from the
thread's perspective) in a queued task, so you have to return to the event
loop for it to be applied.

If a thread is killed by the browser (eg. due to a CPU quota), the
backbuffers it owns are orphaned; you can no longer create contexts for
it.  You need to create a new Canvas.  This isn't great, but workers don't
really seem to try to make it possible to recover from this anyway.

interface CanvasBackbuffer {
    object? getContext(DOMString contextId, any... args);
}

[Constructor(unsigned long width, unsigned long height)]
interface Backbuffer : CanvasBackbuffer {
    void resize(unsigned long width, unsigned long height);
}

interface HTMLCanvasElement : HTMLElement {
    CanvasBackbuffer getBackbuffer();
}

The Backbuffer ctor is available in workers.

Importantly, you can start by just implementing the Backbuffer
constructor.  That would only allow the simpler case of offscreen rendering.

-- 
Glenn Maynard

Received on Thursday, 17 May 2012 01:17:35 UTC