Mobile shaders using high precision everywhere

I am working on optimizing things for mobile VR, and when I look at the generated shaders for OpenGL ES 3.1 (both the viewer in editor and through renderdoc on device), I see that almost all of the math is being done in full high precision. All of the inputs and most of the temp variables in the shaders are declared as “highp float” and “highp vec4”, and in particular every variable in the vertex shader, despite the fact that I have Use Full Precision turned off in all of my materials. This is a huge waste for just about everything I could be doing in the materials for mobile vr, with only static lighting.

Is there some compiler setting I am missing?

I have manually changed all the floats that can be converted to halfs at MobileBasePassPixelShader.usf and MobileBasePassVertexShader.usf.

Most of the math in those files (and other includes I have looked at) is already at half precision, except where the comments say there is a reason for highp (or where it is really needed, like calculating SV_POSITION). It looks like it is even supposed to be using half for the interpolants, but somehow it mostly all comes out as highp in GLSL.

A lot of precision modifiers are clearly being ignored, for example:

UNIFORM_MEMBER_EX(float, DirectionalLightShadowing, EShaderPrecisionModifier::Half)

generates this in GLSL:

highp float PrecomputedLightingBuffer_DirectionalLightShadowing;

It looks like UE4’s handling of shader type precision is completely dysfunctional currently:

In GlslBackend.cpp, when bDefaultPrecisionIsHalf is true, it actually defaults to high precision in GetPrecisionModifier. This looks like a straightforward coding logic bug, and it forces all variables to highp no matter how you declare them in the shader files.

HLSL low precision types like min10float are not parsed at all by the cross compiler, and simply cause mobile shaders to fail to compile.

If you declare a uniform member with EShaderPrecisionModifier::Fixed, it emits variables of type “fixed”, which is not even a valid HLSL keyword. It comes from Nvidia’s Cg language, which Unity uses as a base for its shaderlab language, and mostly looks like HLSL but isn’t.

This is a huge problem for mobile, as it makes shader math take twice to four times as long as necessary to compute. It also fails to make good use of current PC hardware, as the latest Nvidia cards also run at twice the speed with 16 bit floats, but to take advantage of it you need to use min16float. HLSL type “half” is what the shader code is using now, but that type is essentially deprecated and just defaults to 32 bit on all current compilers.

I have been working to try to fix some of these issues, but they are pretty pervasive and it is slow going. Getting it to translate “half” to “mediump float” was fairly easy, and a big speedup, but getting any support for “lowp float” touches a lot of systems because the HLSL parser has no equivalent. UE4 doesn’t get anywhere close to optimal shader performance on mobile currently, and it really holds UE4 back on mobile VR especially.

1 Like

Do you find any solution?
I found mobile precision are still be fp32 instead of fp16 even I use “half”.
Looks like official not aware this for years?