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
(implementations can fudge this, if it's transparent to scripts), the
buffer is cleared immediately when you return from a call which modified
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.
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).