I have a bunch of CG/HLSL shaders. Since I’m in the phase of strongly optimising them for mobile, I started looking more closely to the compiled code and I noticed some weirdness with the precision qualifiers.
Here’s a super simple frag shader:
fixed4 frag(v2f i) : SV_Target
{
fixed4 c = tex2D(_MainTex, i.uv);
c.rgb *= DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.lmuv));
return c;
}
And here is the compiled gles
void main ()
{
lowp vec4 c_1;
lowp vec4 tmpvar_2;
tmpvar_2 = texture2D (_MainTex, xlv_TEXCOORD0);
c_1.w = tmpvar_2.w;
mediump vec4 tmpvar_3;
tmpvar_3 = texture2D (unity_Lightmap, xlv_TEXCOORD1);
lowp vec4 color_4;
color_4 = tmpvar_3;
mediump vec3 tmpvar_5;
tmpvar_5 = (2.0 * color_4.xyz);
c_1.xyz = (tmpvar_2.xyz * tmpvar_5);
gl_FragData[0] = c_1;
}
You’ll notice that it stores the lightmap texture in mediump (?), then casts it to lowp, then doubles it (I’m guessing because of the whole doubldDLR that mobiles use for lightmaps) while putting it in a mediump again, then finally multiplies it with the texture, which is lowp, presumably casting down to it again.
And… Why? I know most mobile devices these days treat lowp and mediump the same, so it probably doesn’t matter, but for some devices it matters. And I can’t think of any reason of why it’s done this way.
I also tried storing the lightmap in a half3 variable, in order to save one cast to lowp, but it results in the exact same compiled code.
Also.
Let’s say that for some reason, I want everything in frag in high precision. It would be pretty stupid thing to do, but let’s say I’m doing something weird and I don’t care for performance.
Here’s my frag shader. Everything is in highp, including the frag itself. So I’m hoping everything will be kept in the same precision and there will be no casts.
float4 frag(v2f i) : SV_Target
{
float4 c = tex2D(_MainTex, i.uv);
float3 lm = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.lmuv));
c.rgb *= lm;
return c;
}
Here’s the compiled.
void main ()
{
highp vec3 lm_1;
highp vec4 c_2;
lowp vec4 tmpvar_3;
tmpvar_3 = texture2D (_MainTex, xlv_TEXCOORD0);
c_2 = tmpvar_3;
mediump vec4 tmpvar_4;
tmpvar_4 = texture2D (unity_Lightmap, xlv_TEXCOORD1);
lowp vec4 color_5;
color_5 = tmpvar_4;
mediump vec3 tmpvar_6;
tmpvar_6 = (2.0 * color_5.xyz);
lm_1 = tmpvar_6;
c_2.xyz = (c_2.xyz * lm_1);
gl_FragData[0] = c_2;
}
Instead of keeping everything in highp (which is what I was expecting), it does the texture in lowp (??), the lightmap in mediump then in lowp then mediump again and then does a bunch of casts to get to a highp. Which is obviously not what I want.
It is quite possible I am missing something, but I don’t understand why Unity overrides my precision choices and does whatever it wants. And I am pretty sure this wasn’t the case in earlier (say, Unity 4 or even 3) versions. Is it a case of Unity deciding they know better (I mean, sure, they probably do) and forced their own precision choices onto everyone? (like that extra blit Android has for compatibility reasons). These things should be an option and should not be forced on everyone. (like a checkbox somewhere that says, “let Unity decide some precisions” or whatever)
Or, I’m missing something and this is needed and normal…
Thoughts?