Three.js examples program linking failure on mobile devices because of mismatched type/precision on 'renderType' uniform #2769

Closed
bjacob opened this Issue Dec 7, 2012 · 23 comments

Comments

5 participants

bjacob commented Dec 7, 2012

Try to run the Three.js examples on a mobile devices. For example, this example:

http://stemkoski.github.com/Three.js/HelloWorld.html

and the mobile device I'm using is a Nexus S running Android 2.3 and Firefox.

In 'adb logcat' I see this WebGL warning:

E/GeckoConsole( 2189): [JavaScript Warning: "Error: WebGL: linkProgram failed, with this log:
E/GeckoConsole( 2189): Uniform variable renderType type/precision does not match in vertex and fragment shader
E/GeckoConsole( 2189):
E/GeckoConsole( 2189): " {file: "http://stemkoski.github.com/Three.js/js/Three.js" line: 695}]

By any chance, is this code simply forgetting a "precision mediump float" in a fragment shader?

vvuk commented Dec 7, 2012

... or more likely forgetting an explicit precision on renderType in the vertex shader (since I believe it'll default to high).

Contributor

alteredq commented Dec 7, 2012

That's some pretty old lib version r49 (April 2012), we are now at r54.

Currently we default to highp unless set otherwise in WebGLRenderer constructor and it should be the same value both in vertex and fragment shaders.

https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js#L16
https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js#L6151
https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js#L6253

BTW I tried some of our examples from here (our current stable master branch r53):

http://mrdoob.github.com/three.js/

And they seem to run fine in Firefox on Nexus S (Android 4.1.2). Not all of them run, but at least some do.

Also works in Firefox on Galaxy Nexus (Android 4.2.1).

vvuk commented Dec 7, 2012

Hm, could be a driver issue in that case on Android 2.3. However, using the same precision for the entire vertex and fragment shader is probably not a good idea -- generally you'd want to use high precision for the vertex shader, especially for vertex coordinates. The fragment shader can often be medium or low (the caveat being that shared uniforms need to be the same in both).

bjacob commented Dec 7, 2012

highp in fragment shaders is not supported on many mobile drivers (and is not mandatory in OpenGL ES 2.0).

Also, you can use getShaderPrecisionFormat to query what is actually supported.

Contributor

alteredq commented Dec 7, 2012

Yup, yup, we will get there eventually. We didn't even start yet with doing any special modifications for mobile.

Whole lib so far is made for desktops and tested on desktops. Speaks pretty well about WebGL that it runs as good as it does without really doing anything at all for mobile. Well done, both on specs and mobile FF implementation ;)

bjacob commented Dec 7, 2012

WebGL content developed with desktops in mind should run on mobile without specific modifications. There just is a short list of mistakes to avoid, and needlessly requiring highp in fragment shaders is near the top of that list.

See e.g.
https://developer.mozilla.org/en-US/docs/WebGL/WebGL_best_practices

Do you have a specific reason to require highp in the fragment shader here?

If yes, fine, at the end of the day some high-end content won't run on insufficient hardware.

Contributor

alteredq commented Dec 7, 2012

Do you have a specific reason to require highp in the fragment shader here?

Not really. We were actually defaulting to mediump, then when BlackBerry folks started to use three.js we asked them, they did tests and found out mediump didn't really make it any faster on their devices (see #1602).

Contributor

alteredq commented Dec 7, 2012

I did a quick test now on Galaxy Nexus - at least for one particular example there doesn't seem to be neither performance nor visual quality difference between highp and mediump, lowp already messes up everything.

https://dl.dropbox.com/u/26786471/tmp/precision/webgl_materials_cubemap1.html
https://dl.dropbox.com/u/26786471/tmp/precision/webgl_materials_cubemap2.html
https://dl.dropbox.com/u/26786471/tmp/precision/webgl_materials_cubemap3.html

highp
mediump
lowp

The same thing on Nexus S, no difference between highp and mediump.

@bjacob Did you have some particular devices in mind for this advice?

Don't explicitly require highp precision in fragment shaders, unless you really need to. Try using mediump instead. Using highp precision in fragment shaders will prevent your content from working on most current mobile hardware. Starting in Firefox 11, the WebGL getShaderPrecisionFormat() function is implemented, allowing you to check if highp precision is supported, and more generally letting you query the actual precision of all supported precision qualifiers.

So far these devices seem not to care much about precision specifier: Nexus S, Galaxy Nexus, Blackberry PlayBook and Xperia Arc.

@mrdoob What do you think? There doesn't seem to be any harm in using mediump default.

bjacob commented Dec 7, 2012

mediump should be the default unless you have a specific reason not to use it, because it is the highest that is guaranteed to be supported everywhere. Before using a non-universal feature like highp in fragment shader, please at least check that it's supported, here using getShaderPrecisionFormat.

Contributor

alteredq commented Dec 7, 2012

Before using a non-universal feature like highp in fragment shader, please at least check that it's supported, here using getShaderPrecisionFormat.

Sounds good. What is a actual mapping between values returned from getShaderPrecisionFormat and GLSL's highp / mediump / lowp?

rangeMin  of type GLint 
The base 2 log of the absolute value of the minimum value that can be represented. 

rangeMax  of type GLint 
The base 2 log of the absolute value of the maximum value that can be represented. 

precision  of type GLint 
The number of bits of precision that can be represented. For integer formats this value is always 0.

Edit: nevermind, I think it's described here:

http://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetShaderPrecisionFormat.xml

Owner

mrdoob commented Dec 7, 2012

@mrdoob What do you think? There doesn't seem to be any harm in using mediump default.

I have no preference really. As long as the output is the same, whatever makes browser vendors happier ;)

Contributor

alteredq commented Dec 7, 2012

@mrdoob It will probably not be the same output for different scenes, for example I would assume precision will matter more for larger outdoor scenes. But that's kinda up to the application layer to optimize.

I like the idea of clamping precision to maximum allowed by the platform proposed by @bjacob.

bjacob commented Dec 7, 2012

The WebGL spec just falls through to OpenGL ES in this case.

http://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetShaderPrecisionFormat.xml

If a high precision floating-point format is not supported for fragment shaders,
calling glGetShaderPrecisionFormat with arguments GL_FRAGMENT_SHADER
and GL_HIGH_FLOAT will return 0 for both range and 
precision.  Support for a high precision floating-point format is mandatory for 
vertex shaders.
Owner

mrdoob commented Dec 7, 2012

@mrdoob It will probably not be the same output for different scenes, for example I would assume precision will matter more for larger outdoor scenes. But that's kinda up to the application layer to optimize.

As far as I understand what they suggest is to have highp by default in the vertex shader and mediump by default on the fragment shader. So I don't think it should matter whether the scene is big or not.

Maybe it matters when the resolution is big? Uhm, I actually can't picture how lower precision would affect the fragment shader output. Maybe mapping blockiness?

Contributor

alteredq commented Dec 7, 2012

@mrdoob Lower precision in vertex shader should make more errors in geometry. Larger the scene more troubles there should be (for example quantization errors and z-fighting).

I would suspect with lower precision on mobile you would get similar problems like we get on desktop for e.g. solar system but already for much smaller scenes.

Lower precision in fragment shader will get you for example ugly color bandings when combining multiple colors.

That's actually something I encountered recently in deferred shading experiments, when trying to squeeze in more data into render targets (and using smaller bit-depth render targets).

Here even high precision which is on desktops is not enough, you have to use floating point render targets to get nice results without bandings.

Contributor

alteredq commented Dec 7, 2012

Just to get the idea about actual ranges, here is what getShaderPrecisionFormat returns on Galaxy Nexus (it's exactly the same for Nexus S):

http://alteredqualia.com/tmp/webgl-maxparams-test/

galaxy nexus

On desktop it's as expected, high precision everywhere, also higher than highp on mobile.

bjacob commented Dec 7, 2012

What you are looking for is section 4.5 is the GLSL ES 1.0 spec,

http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf

In particular, there are specific minimum precision requirements both from the fragment language and for the vertex language; mediump is guaranteed to satisfy the fragment language requirements (range at least -2^14..2^14, mantissa at least 10 bits) and highp is guaranteed to satisfy the vertex language requirements (range at least -2^62..2^62, mantissa at least 16 bits).

So to start seeing any blockiness artifact with mediump in a fragment shader sampling a texture without particularly nasty arithmetic, you would have to use a texture larger than 1024 pixels in one dimension.

alteredq added a commit to alteredq/three.js that referenced this issue Dec 7, 2012

WebGLRenderer: added clamping of user specified precision to maximum …
…available.

For the moment just continuing with having both vertex and fragment shaders using the same precision (not to get uniforms precision mismatch, see #1602).

See #2769

I'm currently working on the WebGL support in Ejecta and stumbled on the same issue: "Uniform precision mismatch for 'renderType'". I get this error with the Walt PointLight demo, which uses the ShaderFlares:
https://github.com/mrdoob/three.js/blob/master/src/extras/shaders/ShaderFlares.js#L109

Because renderType is of type int, it has no default precision in these shaders. Furthermore, the default precision for float should be the WebGLRenderer's _precision property, I guess!?.

Changing the uniform declaration to uniform mediump int renderType; in all 4 shaders fixes the issue for me. Since renderType is just an enum, I guess it could even be set to lowp?

alteredq added a commit to alteredq/three.js that referenced this issue Dec 30, 2012

Contributor

alteredq commented Dec 30, 2012

Good catch. Should be fixed now, thanks.

Awesome work on Ejecta. Which performance do you get for some of the three.js demos you managed to run?

Thanks for the swift fix!

Framerates are pretty good, but it heavily depends on the shaders used. There's really not that much overhead when going through JS instead of interfacing with OpenGL directly in native code.

On my iPhone4S I get about 20fps for the Camaro and 55fps for PointLight Walt. I also tried to get the Unwrapageddon working - it mixes a lot of Canvas2D and WebGL so it's a good test case for Ejecta, plus it would have been awesome on a touch screen. However, the self shadowing brings the iPhone to a grinding halt at about 0.5fps. I haven't really looked into it any closer.

Edit: I had a closer look at the profiler now. That there's not much overhead with JS is unfortunately not as true as I thought. Unpacking plain JS arrays in native code seems to be a bottleneck. Also, array lookup in JS into TypedArrays seems to be quite slow (probably about the same as for plain arrays):

I'm somewhat surprised at the performance gains you got without the JIT.

~ https://bugs.webkit.org/show_bug.cgi?id=103454#c5

Oh, I wish we could use the JIT :/

Contributor

alteredq commented Dec 30, 2012

@phoboslab Sweet ;).

Oh, I wish we could use the JIT :/

I wish they just turned on WebGL that's already there in iOS Webkit :S

I would be curious how these demos run there, if somebody has installed that hack, please tell us ;).

BTW on Galaxy Nexus in mobile Firefox Camaro is ~12-15 fps and that's with much slower HW than iPhone 4S.

Yep, I was actually surprised by this as well. Maybe there's something in the Shaders used by the Camaro example that the iPhone doesn't like? Or maybe there's some (slower) JS stuff going on? I don't know.

As a counter example, the Walt CubeMap runs with 15-22fps on my Nexus, whereas I have rock solid 60fps on my iPhone4S. I guess benchmarking WebGL is even harder to do right, than plain JS benchmarks.

Contributor

alteredq commented Dec 30, 2012

Yep, I was actually surprised by this as well. Maybe there's something in the Shaders used by the Camaro example that the iPhone doesn't like? Or maybe there's some (slower) JS stuff going on? I don't know.

For Camaro JS involvement should be pretty minimal, at least in the runtime. Loading and initialization could be taxing on the CPU but then it's like 99% just the GPU.

Shaders are more complex in Camaro than in Walt CubeMap but should be still quite similar, just more math. You could try to turn off these three flags and see if it makes any difference:

https://github.com/mrdoob/three.js/blob/master/examples/webgl_loader_ctm_materials.html#L163

Also Camaro geometry is heavier than Walt, so there could be some memory issues.

Oh, and there is also antialiasing, Camaro has it, Walt doesn't. That's kinda big one on slower systems, though not sure if you do any antialiasing in Ejecta?

Another thing coming to mind could be that Firefox on Android seem to use smaller resolution than native one. So if Ejecta uses full iPhone 4S resolution, it could be disadvantaged for fragment shader heavier things while on Nexus bottlenecks may be elsewhere.

I guess benchmarking WebGL is even harder to do right, than plain JS benchmarks.

Indeed, especially cross-platform (as in desktop vs notebook vs integrated GPUs vs smartphone/tablet, all having not just radically different performance but also different performance profiles).

@mrdoob mrdoob closed this Oct 19, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment