WebGL Shader precision on MacBook Pro vs iPhone
Photo by David Monje on Unsplash
I learned something new today. When developing in WebGL, you are required to set a default precision for the fragment shader. Here are the options:
precision lowp float;
precision mediump float;
precision highp float;
I read somewhere that lower precision calculations are faster, so if the result is the same you should attempt to use lowp
if possible. I switched shademap.app to use lowp
precision and everything looked good UNTIL I checked it on the iPad and my iPhone. It was broken.
Both were using lowp
, how could this be? Well, it turns out that the precision is not identical across hardware. (I suspect the reason is that the iPhone uses an Apple chip while the MacBook uses an Intel chip). The lack of precision was causing my shade calculations to lose accuracy and the final result was incorrect.
Here is what lowp
, mediump
, highp
correspond to on each device:
MacBook Pro (2018) | iPhone SE | |
---|---|---|
lowp | precision: 23 rangeMin: 127 rangeMax: 127 |
precision: 10 rangeMin: 15 rangeMax: 15 |
mediump | precision: 23 rangeMin: 127 rangeMax: 127 | precision: 10 rangeMin: 15 rangeMax: 15 |
highp | precision: 23 rangeMin: 127 rangeMax: 127 | precision: 23 rangeMin: 127 rangeMax: 127 |
As you can see, if you want the iPhone/iPad and MacBook Pro to use the same precision and range, you will need to set your fragment shader to highp
.
Using another device? You can check its precision settings here or use the following code:
const canvas = document.createElement('canvas');
const div = document.createElement('div');
document.body.append(div);
const gl = canvas.getContext('webgl');
const types = {
lowp: gl.LOW_FLOAT,
mediump: gl.MEDIUM_FLOAT,
highp: gl.HIGH_FLOAT,
};
let output = '';
for (x in types) {
const { precision, rangeMax, rangeMin } = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, types[x])
output += x + ': ' + JSON.stringify({precision: precision, rangeMin: rangeMin, rangeMax: rangeMax}, null, 2) + '\n';
}
div.innerText = output;