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

Re: [Public WebGL] More predictable behavior for !preserveDrawingBuffer

I guess it's not clear whether that behavior is implementable or not, so I'll just give the full details for people to think about.

Currently, !preserveDrawingBuffer's behavior depends on when compositing happens, which is unpredictable.  For example, http://zewt.org/~glenn/webgl-nondeterministic-clearing.html sometimes prints 4294902015 twice, and sometimes prints 4294902015 0, depending on whether compositing happens.

This is an attempt to repair that, without introducing the performance problems of preserveDrawingBuffer (eg. needing to copy each frame to pretend that you're single-buffered).  The idea is that, from the perspective of scripts, draws to the buffer take effect when you return from the _javascript_ call.  As far as scripts can detect (implementations can fudge this, if it's transparent to scripts), the buffer is cleared immediately when you return from a call which modified the buffer.

The "stable state" concept is defined by HTML.  Roughly speaking, a stable state is presented when a script returns to the browser, including between adjacent <script> elements.  See: http://www.whatwg.org/specs/web-apps/current-work/#await-a-stable-state

Add the following:

> Each WebGLRenderingContext has a 
drawing buffer dirty flag, which is initially unset.
> To mark the drawing buffer dirty, the following steps must be performed:
> 1. If the drawing buffer dirty flag is set, terminate these steps.
> 2. Set the drawing buffer dirty flag.
> 3. Asynchronously await a stable state, allowing the task that invoked this algorithm to continue. The synchronous section consists of all the remaining steps of this algorithm until the algorithm says the synchronous section has ended. (Steps in synchronous sections are marked with ⌛.)
> 4. ⌛ Present the backbuffer for compositing.
> 5. ⌛ If the the preserveDrawingBuffer attribute in the context's actual context parameters is false, clear the contents of the backbuffer to their default values.
> 6. ⌛ Clear the drawing buffer dirty flag.

(A lot of this comes directly from HTML.  See http://www.whatwg.org/specs/web-apps/current-work/#update-the-image-data.)

Then, the clear, drawArrays and drawElements definitions say, for example,

> void drawArrays(GLenum mode, GLint first, GLsizei count) (OpenGL ES 2.0 §2.8, man page)
>      Mark the drawing buffer dirty.  If first is negative, an INVALID_VALUE error will be generated.

Step 4 doesn't necessarily mean the buffer is composited; it just means that the contents of the buffer are presented to the compositor for when it is.  In effect, this is triple-buffering: when you return from the script it flips the buffer, so the new contents are seen by the compositor, and the user sees the next empty third buffer.

This helps several things:

- It makes it impossible to see varying behavior if you run two scripts too quickly and no compositing happens (eg. the above sample).
- Similarly, edge cases like successive inline <script> blocks are defined: the buffer will always be cleared.  http://zewt.org/~glenn/webgl-nondeterministic-clearing-2.html will always print 4294902015, 0.
- It's explicit that the clear will happen even if a draw call fails.  (This is probably the only way it can be implemented if you're layered on ES anyway.)
- If the canvas isn't being rendered (eg. it's display: none, scrolled offscreen or not in a document), the script-visible behavior is unchanged.
- It still prevents the major problem of preserveDrawingBuffer == true that I know of (eg. having to pretend that you're single-buffered, copying the buffer each frame).

Glenn Maynard