pex-renderer icon indicating copy to clipboard operation
pex-renderer copied to clipboard

Use getShaderPrecisionFormat to check if highp precision is available in shader

Open dmnsgn opened this issue 6 years ago • 15 comments

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'
})

dmnsgn avatar Jan 02 '19 13:01 dmnsgn

I think in the context of pex-renderer it’s safe to assume such device would not be able to run it anyway.

vorg avatar Jan 02 '19 17:01 vorg

@dmnsgn I'm re-opening this issue due to iPhoneX problems with normal mapping https://github.com/pex-gl/pex-renderer/issues/123

vorg avatar Jan 03 '19 18:01 vorg

PR in progress: https://github.com/pex-gl/pex-renderer/pull/125

dmnsgn avatar Jan 14 '19 09:01 dmnsgn

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.

vorg avatar Jan 14 '19 12:01 vorg

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.

vorg avatar Jan 14 '19 12:01 vorg

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.

vorg avatar Jan 14 '19 12:01 vorg

ThreeJS work "towards mediump"

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.

vorg avatar Jan 14 '19 12:01 vorg

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.

dmnsgn avatar Jan 15 '19 09:01 dmnsgn

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...

vorg avatar Jan 15 '19 17:01 vorg

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?

vorg avatar Jan 16 '19 02:01 vorg

I have never seen any WebGL shader in the wild making use of per variable precision setting (eg. no uniform highp mat4 uModelMatrix;)

dmnsgn avatar Jan 16 '19 11:01 dmnsgn

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)

vorg avatar Jan 18 '19 14:01 vorg

Would it still be possible to have per material precision with this approach?

dmnsgn avatar Jan 18 '19 15:01 dmnsgn

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;

vorg avatar Mar 20 '19 11:03 vorg

I think we should test it our pex-renderer materials with mediump devices and use GL_FRAGMENT_PRECISION_HIGH for portability.

vorg avatar Mar 20 '19 11:03 vorg