[whatwg] Challenging canvas.supportsContext

Ian Hickson ian at hixie.ch
Tue Sep 3 16:13:59 PDT 2013


The long and short of this is that I renamed "supportsContext()" to 
"probablySupportsContext()". It's already implemented in WebKit (and dino 
says he's going to rename it in the implementation also).

Fundamentally, it addresses a need that none of the other proposals 
addressed: how to know whether or not you can expect to be able to do 3D. 
It's not 100% reliable, but then neither would actually attempting to 
create a context, because creating a context is so expensive on some 
platforms that some UAs are going to move to doing it lazily and so you're 
back to exactly the same level of reliability as this API. Plus, it'd be a 
lot more awkward as an API.


On Wed, 19 Jun 2013, Benoit Jacob wrote:
> 
> I'd like to question the usefulness of canvas.supportsContext. I tried to
> think of an actual application use case for it, and couldn't find one.

The use case is libraries like Modernizr that want to do feature detection 
up-front, but don't want a high performance hit on startup.


> However, that only shifts the question to: what is the reason for them 
> to expose such APIs? In the end, I claim that the only thing that we 
> should recognize as a reason to add a feature to the HTML spec, is 
> *application* use cases.

Oh well the use case for knowing whether or not 3D is supported on a 
particular device is straight-forward: you want to know which set of 
assets and logic to download and run.


> So let's look at the naive application usage pattern for supportsContext:
> 
>   if (canvas.supportsContext("webgl")) {
>     context = canvas.getContext("webgl");
>   }
> 
> The problem is that the same can be achieved with just the getContext 
> call, and checking whether it succeeded.

Suppose you have an app that has a 3D feature, but it's not immediately 
used upon startup. For example, a preview window that is displayed on 
request. You want to preload all the code to run the preview window, but 
you need to load different code based on whether the device can do 3D or 
not. So the use case is more:

   if (canvas.supportsContext("webgl"))
     load3DCode();
   else
     load2DCode();

   // 3D code:
   function run() {
     context = canvas.getContext("webgl");
     // ...
   }

You don't want to pay the cost of creating a throw-away 3D context on 
startup just to know which scripts to load. It defeats the whole point of 
not loading all the code up-front.


> Outside of exceptional cases (out of memory...), the slow path in 
> getContext is the *success* case, and again, in that case a real 
> application would want to actually *use* that context.

Not necessarily, as noted above. The <canvas> you're going to draw to 
might not even exist at the point you need to know if it's 3D or not.


> Keep in mind that supportsContext can't guarantee that if it returns true,
> then a subsequent getContext will succeed.

Sure. getContext() can't guarantee that if it returns a context, the 
context will work forever, either.


> The spec doesn't require it to, either. So if the existence of 
> supportsContext misleads application developers into no longer checking 
> for getContext failures, then we'll just have rendered canvas-using 
> applications a little bit more fragile.

That's a risk, yes.


> Another problem with supportsContext is that it's untestable, at least 
> when it returns true; it is spec-compliant to just implement it as 
> returning whether the JS interface for the required canvas context 
> exists, which is quite useless.

That would indeed be useless.


> Given such deep problems, I think that the usefulness bar for accepting 
> supportsContext into the spec should be quite high.

The problem is that there's no alternative solution.


On Wed, 19 Jun 2013, Tab Atkins Jr. wrote:
> 
> This is missing the point.  You don't want to wait until it's actually 
> time to create the context.  Unless you torture your code flow, by the 
> time you're creating a context you should already know that the context 
> is supported.  The knowledge of which context to use is most useful well 
> before that, when you're first entering the app.
>
> Plus, it doesn't matter how late you do the detection - if you do a 
> straight *detection* at all rather than an initialization (that is, if 
> you throw away the context you've just created for testing), you'll 
> still incur the start-up costs of spinning up a context.  Doing that 
> early or late doesn't matter too much - it's bad either way.

Indeed.


> Like @supports, the supportsContext() method can be easy and reliable 
> with a very simple definition for "supports" - it returns true if 
> calling getContext() with the same arguments would return a context 
> rather than erroring, and false otherwise.  No capacity for lying there 
> without breaking sites.

At least, no capacity beyond the same one that getContext() has in the 
first place, namely that it might return a useful object that later stops 
working.


On Wed, 19 Jun 2013, Kenneth Russell wrote:
>> 
> Additionally, though, application developers wanted to be able to ask 
> questions like "is software rendering supported for WebGL":
> 
> http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2012-September/037232.html
>
> I think this second use case is valid. One could imagine a graphing or 
> charting library which would use low-power, software-rendered WebGL if 
> available, but otherwise would fall back to using 
> CanvasRenderingContext2D.

Indeed. This is supported per the spec, assuming getContext() supports the 
relevant arguments.
  

On Wed, 19 Jun 2013, Boris Zbarsky wrote:
> 
> I see.  For this use case, the current definition of supportsContext is 
> of limited utility, unless you make some assumptions about behavior that 
> the spec does not require....

I think what the spec requires is as close as one can get to those 
assumptions without requiring something that can't be implemented, no?


On Wed, 19 Jun 2013, James Robinson wrote:
> 
> What would a page using Modernizr (or other library) to feature detect 
> WebGL do if the supportsContext('webgl') call succeeds but the later 
> getContext('webgl') call fails?

Presumably the same as if the first getContext('webgl') call succeeds 
today, but the next getContext('webgl') call fails.


On Wed, 19 Jun 2013, Kenneth Russell wrote:
> 
> supportsContext() can give a much more accurate answer than 
> !!window.WebGLRenderingContext. I can only speak for Chromium, but in 
> that browser, it can take into account factors such as whether the GPU 
> sub-process was able to start, whether WebGL is blacklisted on the 
> current card, whether WebGL is disabled on the current domain due to 
> previous GPU resets, and whether WebGL initialization succeeded on any 
> other page. All of these checks can be done without the heavyweight 
> operation of actually creating an OpenGL context.

Yeah, it seems to me that the cases where probablySupportsContext() would 
return the wrong result are not that common.


On Wed, 19 Jun 2013, Kenneth Russell wrote:
> 
> Any application which has a complex set of fallback paths. For example,
> 
>   - Preference 1: supportsContext('webgl', { softwareRendered: true })
>   - Preference 2: supportsContext('2d', { gpuAccelerated: true })
>   - Preference 3: supportsContext('webgl', { softwareRendered: false })
>   - Fallback: 2D canvas
> 
> I agree that ideally, if supportsContext returns true then -- without 
> any other state changes that might affect supportsContext's result -- 
> getContext should return a valid rendering context. It's simply 
> impossible to guarantee this correspondence 100% of the time, but if 
> supportsContext's spec were tightened somehow, and conformance tests 
> were added which asserted consistent results between supportsContext and 
> getContext, would that address your concern?

If there's a way to tighten up the requirements in the spec, please do let 
me know. I'm not sure what more to say there right now.


On Wed, 19 Jun 2013, Tab Atkins Jr. wrote:
> 
> It seems like this falls into the canPlayType() bucket, where you can 
> definitely give negatives, but your positives are at best hopeful. Maybe 
> we should just have it do the same thing, and return "maybe" or 
> "probably" rather than a boolean true?

I renamed the method to try to make this clearer.


On Wed, 19 Jun 2013, James Robinson wrote:
> 
> var ctx = canvas.getContext('webgl', { 'allowSoftware': false});
> if (ctx) {
>   doPreference1(ctx);
>   return;
> }
> ctx = canvas.getContext('2d', {'allowSoftware': false});
> if (ctx) {
>   doPreference2(ctx);
> // etc
> 
> how could I simplify this code using supportsContext() ?

"doPreference1()" and friends here are most likely to be "fetch the code, 
create the <canvas>es, create the contexts", etc. Getting the context from 
the feature test all the way to where it is first used seems like a lot of 
complexity.


> It seems overwhelmingly likely that one of the state changes that might 
> affect the result will be attempting to instantiate a real context.

Can you elaborate on this?


> I don't see how supportsContext() could be as accurate as getContext() 
> without doing all of the work getContext() does.  If it's not 100% 
> accurate, when is it useful?

getContext() isn't 100% accurate either, and it's more expensive.


On Wed, 19 Jun 2013, Benoit Jacob wrote:
> 
> In Mozilla code, we fail WebGL context creation if any GL error occurs 
> during initialization, where we have to make a number of specific OpenGL 
> calls (e.g. querying many constants, enabling point sprites...). So it 
> is perfectly possible for WebGL specifically to fail on a given device 
> where OpenGL compositing works, without being specifically blacklisted, 
> and currently we can rely on these automatic checks rather than having 
> to curate a blacklist for these problems, which is very nice.

You could bring these tests forward in the implementation, so that 
probablySupportsContext() itself doesn't have to do them, right?


> Another way in which WebGL specifically can fail, is that some driver 
> bugs cause errors that we may want to ignore in our compositor code but 
> not in WebGL. To give an example, on many Vivante GPU drivers, which are 
> very common in Chinese mobile devices, the first eglMakeCurrent call on 
> a newly created context can return false without any actual EGL error 
> [1]. A browser may want to ignore this error in its compositor to be 
> able to run nonetheless on such devices, but without WebGL support.

Wouldn't you know this before the call to getContext(), though?


On Thu, 20 Jun 2013, Boris Zbarsky wrote:
> On 6/19/13 6:04 PM, Kenneth Russell wrote:
> > supportsContext() can give a much more accurate answer than 
> > !!window.WebGLRenderingContext. I can only speak for Chromium, but in 
> > that browser, it can take into account factors such as whether the GPU 
> > sub-process was able to start, whether WebGL is blacklisted on the 
> > current card, whether WebGL is disabled on the current domain due to 
> > previous GPU resets, and whether WebGL initialization succeeded on any 
> > other page.
> 
> window.WebGLRenderingContext can take things like that into account too, 
> in Gecko.  For what it's worth.

Wouldn't checking for window.WebGLRenderingContext be just as unreliable 
then? I don't understand why it's ok to be able to test that, but why 
probablySupportsContext() wouldn't be ok.


On Wed, 19 Jun 2013, Paul Irish wrote:
>
> On the Modernizr side, an old version did indeed create a context for 
> feature detection. For the past two years we have advocated the soft 
> `"WebGLRenderingContext" in window` test instead. There, of course, is 
> some gap between the results of that detect and how successful a 
> getContext call will be, but I don't have data those false positive 
> rates.

Right, this would let you close that gap a bit.


On Thu, 20 Jun 2013, Boris Zbarsky wrote:
> 
> So that's an interesting question.  Should UAs simply not have 
> window.WebGLRenderingContext in situations in which supportsContext 
> would return false?

probablySupportsContext() takes arguments that affect the answer. (Also, 
the answer can vary over time.)


On Wed, 19 Jun 2013, Boris Zbarsky wrote:
> > 
> > Plus, it doesn't matter how late you do the detection - if you do a 
> > straight *detection* at all rather than an initialization (that is, if 
> > you throw away the context you've just created for testing)
> 
> OK, but why are we making that assumption?  I guess if people insist on 
> doing that, then we do in fact need something that will basically try to 
> guess whether getContext might succeed.

Paul reported that this is common practice, for what it's worth.


> > Like @supports, the supportsContext() method can be easy and reliable 
> > with a very simple definition for "supports" - it returns true if 
> > calling getContext() with the same arguments would return a context 
> > rather than erroring, and false otherwise.
> 
> Just so we're clear, this is _not_ what supportsContext is specified to 
> do. As specced, it will return false if you know for a fact that 
> getContext would return null.  It will return true if you think that 
> getContext might not return null.  This means that a true return doesn't 
> really mean squat about what getContext will do.
> 
> And the reason for that is that you can't tell whether getContext will 
> return null until you try to do it, given how getContext is specced.

I'm happy to try to tighten this up if you have any suggestions.

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


More information about the whatwg mailing list