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

Re: [Public WebGL] How to set a canvas backing store to display units?

On Wed, Jun 13, 2012 at 12:14 PM, Gregg Tavares (社用) <gman@google.com> wrote:
This only works now because the min "max size" (AFAIK) is 2048 and no one has tried to make it bigger.


Please stop screaming at me.

That's not how things work on the Web.  If everyone is already doing something in a certain way, then it becomes the correct way.  Web APIs are bound by web-compatibility.

If you want to render to a framebuffer of the same dimensions as the backing store, then that's simple: create the framebuffer texture using ctx.drawingBufferWidth and ctx.drawingBufferHeight. 

No one is doing this either so your argument that because everyone is doing gl.viewport(..., canvas.width) can't change since everyone is doing it also holds here. Everyone is making fboing canvas.width and canvas.height as well.

No, it doesn't.  Again (for the third time, I think :) if you make the mistake of using canvas.width/height as the size of the FBO (instead of using the size of the drawing buffer), all that usually happens is you get lower resolution output.  This is much more bearable than the breakage that happens if you get the gl.viewport arguments wrong.

That argues that the browser shouldn't be able to pick a different backing store size which was the original intent. The problem drawingBufferWidth is meant to work around is the shitty canvas API that has no way to return an error if you ask for a width that's too big. Even in canvas 2d this is a problem as in canvas.width = 10000000

Of course, we can make this opt-in, eg. canvas.getContext("webgl", {allowHighRes: true}).  2d canvas doesn't do this because the design is intended to just "always work" for high-res screens, instead of making high-res screens a second-class citizen.  I don't think making this opt-in is necessary, though.

In fact, that's what people should be doing under *both* interpretations of gl.viewport().  This is unaffected by whether the drawing buffer and hardware pixels are 1:1 or not.

No it's not. blending operations, read options all matter based on pixels

Blending operations are unaffected by this: they work on pixels in the backing store.  It makes no difference whether the pixels in the backing store are 1:1 to hardware pixels.

gl.copyTexImage2D(..., width, height)?

What size texture do I get? Is it different if a 640x480 fbo is attached vs a canvas with width = 640x480 ?

This is in the same boat as readPixels: copyTexImage2D would need to treat width and height as CSS pixels, for compatibility, adding a separate copyTexImageDirect2D method which works on device pixels.  In the proposal below, you'd get a 640x480 texture from this call in every case, but when sampling the drawing buffer it may actually read a larger or smaller portion of the buffer and resize it to 640x480.

OpenGL is a device pixel API not a virtual pixel API.

WebGL renders to a backing store of a particular size.  The backing store is then handed off for compositing, where any number of things can happen.  Not only might it have a different @width and @height as the backing store, it may also be scaled (<canvas width=1000 height=1000 style="width: 150px; height: 150px;">), overlaid by another DOM node, have a transform applied to it (<canvas style="transform: skew(45deg);">), and anything else that you can apply to a DOM node.  WebGL doesn't render to device pixels at all.

Here's a more concrete proposal for how to solidify this:

- gl.viewport and gl.stencil explicitly take CSS pixels when no framebuffer is active.  The correct user behavior is to pass in canvas.width and canvas.height (assuming they want the viewport to cover the whole canvas), not the size of the drawing buffer.  This is what people are already doing; this would just make this explicit in the spec.
- When no framebuffer is active, gl.readPixels, gl.copyTexImage2D, gl.copyTexSubImage2D and any other functions where people are using (intentionally or otherwise) CSS pixels also convert units.  The read data will be resampled to the requested size, so if you do readPixels(0, 0, 100, 100) on a high-res 2:1 screen, it'll read 0x0-200x200 from the drawing buffer and resize it to 100x100 before returning it.  This allows for compatibility with existing apps that pass in CSS pixels, by abstracting away the difference in backing store size.
- gl.readPixelsDirect, gl.copyTexImageDirect2D and gl.copyTexSubImageDirect2D behave as their non-direct equivalents without the CSS conversion, taking pixels in the active framebuffer or backing store.  This is what people should actually be using, to get full-resolution, unresampled image data.  The non-"direct" methods become for compatibility only.
- Optionally--I'm not sure if this is still needed with the above--add canvas.getContext("webgl", {allowHighRes: true}).  If not set, returning a backing store which is higher resolution than the CSS dimensions of the canvas is not allowed.

("Direct" versions of viewport and stencil could also be added; I'm not sure if they're needed.)

I'm sure there are other details, but this seems like a reasonable start for clearing up how to handle backing stores of a different size than the canvas without breaking compatibility.

Glenn Maynard