Use getShaderPrecisionFormat to check if highp precision is available in shader
API could default to highp and be overrided manually, but check getShaderPrecisionFormat in every case:
renderer = createRenderer(opts)
const renderer = createRendererer({
...,
precision: 'highp' // 'lowp' || 'mediump'
})
I think in the context of pex-renderer it’s safe to assume such device would not be able to run it anyway.
@dmnsgn I'm re-opening this issue due to iPhoneX problems with normal mapping https://github.com/pex-gl/pex-renderer/issues/123
PR in progress: https://github.com/pex-gl/pex-renderer/pull/125
From Unity docs: Precision, Hardware Support and Performance
One complication of float/half/fixed data type usage is that PC GPUs are always high precision. That is, for all the PC (Windows/Mac/Linux) GPUs, it does not matter whether you write float, half or fixed data types in your shaders. They always compute everything in full 32-bit floating point precision.
That explains no performance gains when i tested "half-float pipeline" (and all mediump qulifiers you removed in https://github.com/pex-gl/pex-renderer/pull/125)
The half and fixed types only become relevant when targeting mobile GPUs, where these types primarily exist for power (and sometimes performance) constraints. Keep in mind that you need to test your shaders on mobile to see whether or not you are running into precision/numerical issues.
I did run into issues many times that's why i tried to avoid it.
Using lower precision can often be faster, either due to improved GPU register allocation, or due to special “fast path” execution units for certain lower-precision math operations. Even when there’s no raw performance advantage, using lower precision often uses less power on the GPU, leading to better battery life.
Now that's interesting.
A general rule of thumb is to
start with half precision for everything except positions and texture coordinates. Only increase precision if half precision is not enough for some parts of the computation.
In that context this comment from Filament docs makes more sense
This solution solves the storage problem but requires intermediate computations to be performed with single precision floats. We would instead prefer to perform all (or at least most) of the lighting work using half precision floats instead. Doing so can greatly improve performance and power usage, particularly on mobile devices. Half precision floats are however ill-suited for this kind of work as common illuminance and luminance values (for the sun for instance) can exceed their range. The solution is to simply pre-expose the lights themselves instead of the result of the lighting pass. This can be done efficiently on the CPU if updating a light's constant buffer is cheap. This can also be done on the GPU, as shown in listing 25.
From Apple's [OpenGL ES Programming Guide] (now deprecated of course):
When in doubt, default to high precision. Colors in the 0.0 to 1.0 range can usually be represented using low precision variables. Position data should usually be stored as high precision. Normals and vectors used in lighting calculations can usually be stored as medium precision. After reducing precision, retest your app to ensure that the results are what you expect.
ThreeJS work "towards mediump"
- Textures Rendered as Black on (some?) devices with Android 7+ lot's of horror stories as it seem that they are not mediump ready
- Materials: Consider to use mediump for all shaders more up to date discussion
I kind of proves my earlier comment that just supporting mediump won't magically fix the devices not having highp and would require setting mobile support as a serious goal with serious mobile testing.
It probably won't fixes all bugs magically but at least provide support for other precisions which could be a good start before planning anything else.
I'm not sure about this approach
const renderer = createRendererer({
...,
precision: 'highp' // 'lowp' || 'mediump'
})
After reading all the references above it seems that correct way is to have everything as low as possible because without bugs a) save energy on mobile, b) on desktop it doesn't matter (i.e. it's always "highest quality"). Maybe i need to learn more about ThreeJS approach but the "everything is mediump" way implemented in https://github.com/pex-gl/pex-renderer/pull/125 might be hard to manage as individual uniforms might need to be tweaked (e.g. positions hi/med, colors low etc). Investigating...
BabylonJS does string replacement on all shaders if highp is not supported. Hmm. So is anybody doing per variable precision optimisations beside some autogenerated/cross-compiled shaders in AAA engines like unity?
I have never seen any WebGL shader in the wild making use of per variable precision setting (eg. no uniform highp mat4 uModelMatrix;)
If we are not interested in per-variable precision tweaking why not automate the whole thing in pex-context (e.g inside pipeline()) and get rid of getMaxPrecision in favour of capabilities.fragmentPrecisionHigh boolean or GL_FRAGMENT_PRECISION_HIGH glsl define for simpler shaders switches or troubleshooting if necessary (https://github.com/pex-gl/pex-context/pull/60#issuecomment-455558438)
Would it still be possible to have per material precision with this approach?
I have never seen any WebGL shader in the wild making use of per variable precision setting (eg. no
@dmnsgn
#version 300 es
precision mediump float;
precision mediump int;
layout(std140) uniform FrameUniforms
{
highp mat4 viewFromWorldMatrix;
highp mat4 worldFromViewMatrix;
highp mat4 clipFromViewMatrix;
highp mat4 viewFromClipMatrix;
highp mat4 clipFromWorldMatrix;
highp mat4 worldFromClipMatrix;
highp mat4 lightFromWorldMatrix;
highp vec4 resolution;
highp vec3 cameraPosition;
highp float time;
or
precision mediump float;
precision mediump int;
layout(std140) uniform PostProcessUniforms
{
vec2 uvScale;
float time;
float yOffset;
} postProcessUniforms;
uniform mediump sampler2D postProcess_colorBuffer;
in highp vec2 vertex_uv;
I think we should test it our pex-renderer materials with mediump devices and use GL_FRAGMENT_PRECISION_HIGH for portability.