[whatwg] Canvas in workers

Glenn Maynard glenn at zewt.org
Sun Oct 13 09:12:03 PDT 2013

 On Sat, Oct 12, 2013 at 11:12 PM, Kyle Huey <me at kylehuey.com> wrote:

>    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).

You can transfer data to a worker that's cross-origin, if you have a
MessagePort where the other side goes to another origin's worker (possibly
given to you via eg. window.postMessage).

Is the real goal here trying to limit this to threads and avoid IPC, or is
this actually a cross-origin concern?

   2. Add a worker-only WorkerCanvas constructor that takes the desired
>    width/height of the drawing surface.

This looks like it's trying to allow entirely off-screen rendering within a
Worker, which is fine, but there's no way to resize the backing store in
this mode.  I don't know if that would need a separate subclass of
WorkerCanvas to allow making width/height writable.

>       - 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.

> >   CanvasRenderingContext2D? getContext2D(any... args);
> >
> >   WebGLRenderingContext? getContextWebGL(any... args);

This is crazy.  The platform is inconsistent enough.  We have an API for
this already, getContext(); don't add a different API for the exact same

   5. Add a "commit" method to WorkerCanvas.  For a WorkerCanvas obtained

I'm not sure what this is for.  If you draw in a worker and return without
calling .commit(), is the commit implicit when you return to the event
loop?  (See below for where this matters.)

> 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.

This sounds like it's easy to get wrong, since it'll probably be rare.  An
exception might be better, so if you don't handle it you at least get an
error logged to the console.

There will be flicker issues with this.  The canvas is cleared when you
change width or height.  In the UI thread that's OK, since the author can
synchronously redraw immediately after changing the size.  Here, it's
likely it won't be redrawn in time, so it'll flicker whenever the size
changes, especially if it's being changed smoothly.  Here's a suggestion to
fix this:

- When the UI thread changes Canvas.width or Canvas.height, it doesn't
actually resize buffers.  Instead, it sends a message to the WorkerCanvas
asking for the change.  Until the change actually happens, the Canvas
continues to be composited as before.  (However, the change to .width and
.height is visible on the object immediately.)
- When the WorkerCanvas's event loop receives a message asking for a size
  - Change the size of the back-buffer, and update WorkerCanvas.width and
WorkerCanvas.height accordingly.
  - Fire onresize on the WorkerCanvas.  The worker is expected to redraw
here.  (This is where the "implicit commit" matters: we want to guarantee a
commit here.)
  - Only when the newly-redrawn buffer is committed does the front buffer's
size get updated to match the back-buffer.

In other words, when you change the size in the UI thread, it continues to
composite the same image (possibly not filling the whole element, or being
stretched) until the worker actually gets the resize and has a chance to
redraw it.

This also means the idea of not being able to commit because of a resize
can go away, and commit() can be void, since the back-buffer size never
actually changes while the worker is drawing.

On Sun, Oct 13, 2013 at 11:01 AM, David Bruant <bruant.d at gmail.com> wrote:

>    bool commit();
>> Boolean as return value for success? :-s
> A promise instead maybe? throw instead of false at least?
> In any case, it looks like commit could be a long operation (tell me if
> I'm wrong here. Do you have numbers on how long it takes/would take?),
> having it async sounds reasonable.

This should be synchronous and never block.  Even if the Canvas is in a
different process, it should be possible to do this with IPC without
waiting for the other side to process the change.

Glenn Maynard

More information about the whatwg mailing list