[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Public WebGL] using the same context with multiple canvases

On Tue, Nov 13, 2012 at 8:35 AM, Ian Hickson <ian@hixie.ch> wrote:
On Sun, 11 Nov 2012, Brandon Jones wrote:
> On Nov 11, 2012 10:47 PM, "Ian Hickson" <ian@hixie.ch> wrote:
> >
> > When would you want one canvas done one way and another a different
> > way, for the same rendering context?
> There's several reasonable explanations for having differing properties
> per-canvas. Consider Gregg's example of a Maya like tool: it would be
> sensible to request an antialiased canvas for the main editing view, but
> for performance reasons request a non-antialiased canvas for a texture
> preview widget.

The viewport dimensions are on the context currently, right? Why not put
these on the context as well, and just have the author update them when
they switch canvas?

There's some clarification needed here about what you are referring to when you say "viewprot". In WebGL there is a function. gl.viewport that specifies the affine transformation of x and y from normalized device coordinates to window coordinates and happens to clip vertices (but not pixels).

The system can not set that automatically because it doesn't know what the user is using it for. The user could have set it explicitly to various values including for rendering to framebuffer objects. Except for initialization the browser can't know when it's appropriate to set. Only the app knows.


On Mon, 12 Nov 2012, Gregg Tavares (社ç~T¨) wrote:
> How about
>    db = new DrawingBuffer(creatationAttrubites);
> for gl
>    gl.bindDrawingBuffer(db);
> for canvas 2d???
>    ctx.setDrawingBuffer(db);

Why have two different APIs?

We already have 2 apis that don't share a single function name or signature. Each one should do what feels natural for its api.

> > Today, when does a WebGLRenderingContext "commit" or "draw" to the
> > currently assigned canvas? Is it just when the event loop spins, or is
> > there an explicit "paint a frame now" method?
> It's currently supposed to be when the current event exits IMO but the
> spec is ambiguous saying something about when it's compositied which
> means it's browser specific if other events come by before compositing
> happens.

So you if drawImage() a GL canvas half-way through a script, it draws the
previous image state (as it stood the last time the event loop span), not
the mid-way state like the 2D canvas?

In the drawImage case you get the latest results including the latest draw commands. 

The part that's undefined is this

setTimeout(renderCircle, 1);
Whether you see a circle, a rectangle, or a circle and a rectangle is undefined

I'm not a fan of a DrawingBuffer object because it's yet more indirection,
and we have a pile of indirection here already with all the cross-worker
canvas stuff. I could see having the contexts implicitly have their own
drawing buffer while they aren't bound to a canvas, and having these
buffers be reset (along with state, in the 2D world, though not in GL)
when you bind/unbind, but I don't see why we'd want to explicitly have an
object for this.

We need canvas contents separate from contexts for all the reasons explained in
previous posts. Anything that makes the content specific to the context doesn't
solve the problems we're trying to solve. We need those problems solved whether
it's through DrawingBuffer or not.

DrawingBuffer makes sense because it's what's actually in the browser. We're
just exposing it. It's effectively the framebuffer objects that represent the 
contents of a canvas.

Whether in canvas 2D or WebGL, both of them, when hardware accelerated,
have one or more framebuffer objects that represents their drawingbuffer.

BTW: The term DrawingBuffer comes from the WebGL spec which has 
a creation flag "preserveDrawingBuffer".

If GL doesn't need in-between state (i.e. if drawImage() for GL always
draws the last committed image, not the current state), and if we're ok
with using this model for 2D as well when in a worker, I think the
simplest solution is:

- have two ways to get a context: via getContext(), and via a constructor

- the getContext() method is the current method, it's always fixed to the
same canvas it was created from, and can't be moved; drawImage() in the 2D
world gets the current state

- the constructor gives you back a context with its own implicit backing
store; you can set its dimensions dynamically using constructor arguments
and can change the dimensions later on the context itself; this object can
be bound to a canvas (or CanvasProxy for workers) later

- when you bind a context to a canvas, it blows away its bitmap (and
state, for 2D, though not for GL), and drawing commands start going to the
canvas. The bitmap is now just the canvas bitmap; drawImage() and, in 2D,
getImageData(), take the last committed image; you can commit the drawing
commands to that bitmap using commit() or spinning the event loop

Why should 2D be different than GL?

Again, this is not a solution for us. If we want to make apps like those
linked to above we can't have canvases having their contents blown 
away when we move the context between them and it makes no sense 
for us to have a backstore specific to a context.

There's also the problem that blowing away the canvas backstore
each time you move the context and having to reset the size
of the context's backstore to match the size of the canvas you 
just attached to means reallocating a very heavy object every time
you change canvases. Yet another reason to keep the storage
separate from the context.

- drawImage() now additionally takes a context, but it always refers to
whatever the context's bitmap looks like (the last committed image,
except for the case of getContext('2d') since that implicitly commits
after each command, as noted above) 

drawImage should take a DrawingBuffer since that represents the 

I'd say for contexts that are created by calling new CanvasRenderingContext()
or new WebGLRenderingContext() there should never been any implicit

So, for old style getContext stuff you'd have the implicit commits
of drawImage, getImageData, toDataURL, and on composite
(though I which that would be changed to on event exit)

For constructed contexts you'd never have an implicit commit.
You must call commit(). The compositor and drawIamge, getImageData,
toDataURL always use the last committed data. Again, in the drawingBuffer.


- the way to spec this is probably by having a virtual "current" bitmap
that all commands go to, but which is not directly accessible from JS

- binding a new context to a canvas unbinds the previous context bound to
it, at which point it goes back to just having an implicit bitmap

- the implicit bitmap can be implemented lazily; it's not needed unless
someone tries to read from it at which point you just need to execute the
commands that were queued against it when it was last commit()ted.

Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'