[whatwg] Canvas in workers

I talked at length with Robert O'Callahan about what the DOM API for
supporting <canvas> in web workers should look like and we came up with the
following modifications to the spec.

   1. Rename CanvasProxy to WorkerCanvas and only allow it to be
   transferred to workers.  I don't think we're interested in supporting
   cross-origin <canvas> via CanvasProxy (I would be curious to hear more
   about what the use cases are).
   2. Add a worker-only WorkerCanvas constructor that takes the desired
   width/height of the drawing surface.
   3. Remove the rendering context constructors and the setContext method
   on WorkerCanvas (née CanvasProxy).
   4. Copy all of the sensible non-node related things from
   HTMLCanvasElement to WorkerCanvas.  This would include
   - width and height as readonly attributes
      - getContext (to replace what we removed in step 3).  roc prefers to
      have getContext2D and getContextWebGL, and dispense with the string
      argument version entirely, but I don't have strong feelings.
      - toBlob.  We do not intend to implement toDataURL here.
   5. Add a "commit" method to WorkerCanvas.  For a WorkerCanvas obtained
   from a main thread <canvas> element, this would cause the buffer displayed
   on screen to swap.  For a WorkerCanvas created *de novo* on a worker
   thread, it would do nothing.  This commit method would also commit a minor
   violation of run-to-completion semantics, described below.
   6. We would rely on extracting ImageBitmaps from the WorkerCanvas and
   shipping them to the main thread via postMessage to allow synchronizing
   canvas updates with DOM updates.  We explored a couple other options but we
   didn't come up with anything else that allows synchronizing updates to
   multiple canvases from a worker.  This isn't really sketched out here.

So the IDL would look something like:

> [Constructor(unsigned long width, unsigned long height)]
>
> interface WorkerCanvas {
>
>   readonly attribute unsigned long width;
>
>   readonly attribute unsigned long height;
>
>
>   CanvasRenderingContext2D? getContext2D(any... args);
>
>   WebGLRenderingContext? getContextWebGL(any... args);
>
>
>   void toBlob(FileCallback? _callback, optional DOMString type, any...
> arguments);
>
>
>   bool commit();
>
> };
>
> WorkerCanvas implements Transferable;
>
Everything would be behave pretty much as one would expect, except perhaps
for the commit method.  The width and height of the canvas can be modified
on the main thread while the worker is drawing.  This would fire an event
off to the worker to update the WorkerCanvas's dimensions that would be
scheduled as if the main thread had postMessage()d something to the
worker.  But it's possible that the worker would attempt to draw to the
<canvas> before that update runs.  It's also possible that the worker would
simply draw in a loop without yielding.  To solve this, if commit is called
and the current dimensions on the main thread don't match the dimensions of
the WorkerCanvas it would fail (return false) and update the dimensions of
the WorkerCanvas before returning.  This is technically a violation of
run-to-completion semantics, but is needed to support workers that do not
yield.

Thoughts?

- Kyle

Received on Sunday, 13 October 2013 04:13:12 UTC