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

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

On Mon, Oct 22, 2012 at 2:57 PM, Vladimir Vukicevic <vladimir@mozilla.com> wrote:
On 10/22/2012 1:42 PM, Gregg Tavares (社用) wrote:

On Mon, Oct 22, 2012 at 8:00 AM, Vladimir Vukicevic <vladimir@mozilla.com> wrote:

My thoughts... ctx1/ctx2 should be equivalent, so no need to specify draw/clear with no binding behaviour, different loss behaviour, etc.  ctx.canvas always references the creating canvas; we don't really have much need to get the canvas from the context anyway. 

I get the canvas from the context all over the place in my code. Since I can get the width/height of various things that way





     function takeScreenshot(gl) {
        return gl.canvas.toDataURL();

etc. I means I don't have to pass both a context and a canvas. If ctx.canvas does not point to the canvas currently being rendered to that would break lots of my code.

Fair enough, I suppose gl.canvas can just switch around to point at the current one.  That would at least let libraries treat the gl object in a consistent way regardless of how many canvases the developer is drawing to.

Given 2 canvases of different sizes you'd need to set the viewport after switching. It would arguably be nice to be able to do this

    ctx.bindFramebuffer(ctx.FRAMEBUFFER, someCanvas);
    ctx.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
    var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    perspectiveMatrix = someMathLib.perspective(fieldOfView, aspect, zNear, zFar);

I'm also not sure that gl.bindFramebuffer(..., canvas) makes sense vs a new function like gl.setDrawingBuffer(canvas). Conceptually its the same but it's not really because ...

   gl.bindFramebuffer(gl.FRAMEBUFFER, canvas);
   gl.framebufferTexture2D(...)  // should fail
   gl.framebufferRenderbuffer(..) // should fail
   gl.deleteFramebuffer(canvas);  // should fail

We already have a special case where these all fail -- where the framebuffer is the special object 'null'.  It's really just extending that special case...

Where as a new function like say gl.setDrawingBuffer(canvas) would make it clear it's "special".  Then this

   gl.bindFramebuffer(gl.FRAMEBUFFER, mybuffer);

would still render to mybuffer. To render to the current canvas/drawingBuffer you'd still do

   gl.bindFramebuffer(gl.FRAMEBUFFER, null);  // render to current drawingBuffer.

This feels a lot more confusing to me -- we already have framebuffer machinery that's well defined, and the current framebuffer is conceptually "where rendering goes".  Introducing new API for switching canvases seems like it would just add an unnecessary layer to this.  Additionally, if we do, then in the future we'll always have to consider our canvas-targetting functions for how they interact with extensions, etc.  If we just define canvases to be framebuffers, and use the regular framebuffer approach, then we reduce the amount of work going forward.  It feels much cleaner to me, and a lot less developer-thought required ("Which framebuffer is bound? That's where drawing is going."  vs. "Which framebuffer is bound and which canvas is bound?  Is the framebuffer null?  Ok then it's going to the currently bound canvas, otherwise it's going to the currently bound framebuffer.")

I see your point. I'm just thinking off all the exceptions you have to write into the spec.

gl.bindFramebuffer(gl.FRAMEBUFFER, canvas);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);  // this is the same as the previous call if that canvas was already bound?

gl.bindFramebuffre(gl.FRAMEBUFFER, myFBO);
gl.getParamater(gl.FRAMEBUFFER_BINDING);   // returns myFBO

gl.bindFramebuffre(gl.FRAMEBUFFER, null);
gl.getParamater(gl.FRAMEBUFFER_BINDING);   // returns null

gl.bindFramebuffre(gl.FRAMEBUFFER, canvas);
gl.getParamater(gl.FRAMEBUFFER_BINDING);   // returns null??? But I just bound something

gl.bindFramebuffer(gl.FRAMEBUFFER, myFBO);
gl.framebufferTexture2D(gl.FRAMEBUFFER, ...);  // works

gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, ...);  // fails because there is no FBO

gl.bindFramebuffer(gl.FRAMEBUFFER, canvas);
gl.framebufferTexture2D(gl.FRAMEBUFFER, ...);  // fails?? I just bound something. Seems like It should succeed.

I can change the size of an FBO like this

  gl.bindFramebuffer(gl.FRAMEBUFFER, myFBO);
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(gl.TEXTURE, 0, gl.RGBA, newWidth, newHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

But to change the size of the canvas I can't do that. I've got to call other functions. If those are different why not this one?

Another thing to consider. If we ever do support shared resources, per the ES spec FBOs can not be shared. So the drawingBuffer would not really be an FBO. It would be a texture (for color) and a 1 or more renderbuffers (for depth/stencil) and would have to be a different FBO in each context that uses it. So it's not really binding the same FBO in that case. Each context internally would probably have an "drawingBufferFBO" and you'd end up internally calling 

WebGLRenderingContext::bindDrawingBuffer(HTMLCanvasElement* canvas) {
   // bind the context specific fbo for it's simulated backbuffer.
   glBindFramebuffer(GL_FRAMEBUFFER, context->drawing_buffer_fbo);

   // attach the shared texture and renderbuffer to the unshared fbo object.
       GL_TEXTURE_2D, canvas->drawing_buffer_color_texture_, 0);
       GL_RENDERBUFFER, canvas->drawing_buffer_depth_renderbuffer_);

   // restore the current fbo in the context (or not?)
   glBindFramebuffer(GL_FRAMEBUFFER, context->current_fbo ? context->current_fbo : context_drawing_buffer_fbo);


   - Vlad

(We can and maybe should introduce something like ctx.getAttachedCanvases() or something?)  ctx.getContextAttibutes() we can extend to be ctx.getContextAttributes(canvas), with default of null being the original canvas (i.e. canvas1).

    - Vlad

- Likewise, allowing canvases from different principals is going to be a source of pain, so I'd make it a requirement that canvases sharing a context must be on the same principal.


On 12-10-17 09:04 PM, Gregg Tavares (社用) wrote:
I'd like to pursue this idea further that Florian brought up which is that, for drawing to multiple canvases, rather than share resources across contexts it would be much nicer to just be able to use the same context with multiple canvases. Something like

    ctx1 = canvas1.getContext("webgl");

Clear's both canvas1 and canvas2 to green

Issues: Under the current WebGL the only way to setup a canvas is by calling getContext(..., contextCreationParameters). Whether the canvas is RGB or RGBA, has depth or stencil, is premultiplied or not, has it's backbuffer preserved or not, etc..

Which leads to some questions.

Is the current API okay. You'd create 2 contexts still but just use one of them as in

    ctx1 = canvas1.getContext("webgl". { alpha: true } );
    ctx2 = canvas1.getContext("webgl". { alpha: false } );

After the cal to cavnas2.setContext(ctx1) what does ctx2 do? Does it still draw to cavnas2 as well? Does it get GL_INVALID_FRAMEBUFFER_OPERATION if a draw/clear function is called and no FBO is bound? Does it become lost? What does ctx1.canvas reference and what does ctx2.canvas reference? What does ctx1.getContextAttributes() return? The attributes for canvas1 or canvas2? 

I think arguably being able to setContext on a canvas is the right way to solve drawing to multiple canvas with the same resources but there are some unfortunate existing API choices.