This post has been de-listed
It is no longer included in search results and normal feeds (front page, hot posts, subreddit posts, etc). It remains visible only via the author's post history.
I'm writing up some notes on what I've learned about the consequences of declaring (and relying on) precision highp float;
versus precision mediump float;
in a fragment shader. In terms of use case, I'm planning to do some ray-marching and Shadertoy-like demos (meaning I will probably be doing per-pixel calculations that involve "world" coordinates), and I don't particularly care about my code being able to run on old phones right now, so maybe I should just set highp
and be done with it. However, I don't know exactly where this code will lead yet, and I'm researching the topic now, so I want to write down what I've learned.
I'm an experienced developer, but I don't have production experience with 3D graphics (at least, not in the last 20 years; I did ship some OpenGL code way back in the day). I'd love for people to chime in and confirm whether the below is correct.
First of all, to describe the minimum precision requirements demanded by each of highp
, mediump
, and lowp
:
highp
requires at least the range and precision of a 24-bit float, with 1 bit sign, 7 bits exponent, 16 bits of significand.mediump
requires the range and precision of a 16-bit float (a.k.a. half-precision float), with 1 bit of sign, 5 bits of exponent, and 10 bits of significand.lowp
is extremely low precision, and the spec can be satisfied by a 10-bit fixed point format, with 1 sign bit, 1 bit of integer part, and 8 bits of fraction part.
Note: For some reason, many sources (including reference 1, below) say highp is at least 32-bit, though this is not correct. The spec (ref 2) only speaks in terms of numerical range and precision, not particular floating-point formats, but it implies 24 bits are the minimum required, not 32 bits. If the spec called for 32 bits, it would call for: 1 sign bit, 8 bits of exponent, and 23 bits of significand. Sources like reference 3 additionally confirm that 24 bits may be used in practice, not just theory, writing: "highp: Typically implemented by 24 bit float" (though this resource is from 2005). See also ref 6 which says for highp, "think float24."
There are two potential reasons to not just write all your fragment shaders to use a highp
amount of precision and declare precision highp float
:
- Not all devices (e.g. phones, tablets) necessarily support
highp
, because it's optional in the spec. If you declare it and it isn't supported, your code won't run. If you rely on ahighp
amount of precision but don't declare it, your graphics will look weird on some devices. - Using a lower precision could be faster, when you don't need the precision of
highp
Before we examine these in detail, it's important to note that on your desktop/laptop, highp
, mediump
, and lowp
are likely to all be 32-bit floats! On such a device, declaring precision has no effect. There is no performance benefit to using mediump
(say) over highp
. You can't even reproduce the effects of running your shader on a device that has lower precision.
According to reference 4:
For the most part [highp, mediump, and lowp] only matter on mobile. The spec says an implementation can always use a higher precision so on desktop both the vertex shader and fragment shader run in highp always. (I know of no desktop GPUs for which this is not true)
Ok, what about point (1) above, the problem of devices that don't support highp
, particularly in fragment shaders? I can't find a list online anywhere, or any hard data, about what devices support what number of bits. (No one links to such a list, in online discussions when it would resolve the question at hand, so I suspect such a thing doesn't exist, but I would love to know if it does.) However, people tend to say that most iOS and Android devices shipped in the last several years (as of the time of this writing, January 2023) do support highp
in fragment shaders.
According to reference 4:
highp in the fragment shader is an optional feature and some mobile GPUs don't support it. I don't know what percent that is in 2019. AFAIK most or maybe even all phones shipping in 2019 support highp but older phones (2011, 2012, 2013) don't.
On the other hand, a GitHub comment (reference 5) lists several mobile devices that are claimed to not support highp
in fragment shaders, including the Samsung Galaxy S20 Pro which was released in 2020. Still, the word on the street seems to be that even "older" iPhones and some older Android phones do support highp
in fragment shaders.
To point (2), about performance: If a device supports mediump
and highp
, and it uses a different number of bits for them (so we would be talking about a mobile device here, probably), then the performance gains of using mediump
can be real.
As far as what to do about all this, reference 4 offers the following practical strategies as options:
For Mobile and Tablets then there are several answers. There is no best. It's up to you
- use the lowest precision you can that still does what you need it to do.
- use highp and ignore the perf issues and the old phones where it doesn't work
- use mediump and ignore the bugs (See below)
- check if the user's device supports highp, if not use different shaders with less features.
Reference 6 has suggestions as well.
It's worth noting that the precision of mediump
seems extremely limited if you are looking to do anything "interesting" in your fragment shader (at least to me; this is a very subjective statement). Reference 4 gives an example of a simple lighting calculation that breaks with mediump
. On the other hand, it's enough for doing texture lookups and calculations with surface normals, and maybe everything you might want to do with texturing and lighting of 3D models for your game or application.
If there is a goal of supporting devices that only have 16-bit floats and no highp
in fragment shaders, it seems to me that code would have to be written carefully with this limited precision in mind, and tested on a device that supports 16-bit floats (and does not promote all floats to 24 or 32 bits).
References:
- https://webglfundamentals.org/webgl/lessons/webgl-precision-issues.html
- Section 4.5 of https://www.khronos.org/files/opengles_shading_language.pdf
- https://fileadmin.cs.lth.se/cs/Education/EDA075/misc/GLESSL.pdf
- https://webglfundamentals.org/webgl/lessons/webgl-qna-when-to-choose-highp--mediump--lowp-in-shaders.html
- https://github.com/ashima/webgl-noise/issues/25#issuecomment-1079668798
- https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#in_webgl_1_highp_float_support_is_optional_in_fragment_shaders
Subreddit
Post Details
- Posted
- 2 years ago
- Reddit URL
- View post on reddit.com
- External URL
- reddit.com/r/GraphicsPro...