# Weird shader behavior on different devices

I'm trying to combine an ordinary shader with a Single Distance Field shader (for fonts and scalable icons) into one shader program. The idea is simple - if uv.x is less than 1.0, it is drawn as usual, and if more than 1.0 then SDF is used (using GL_REPEAT uv.x and uv.x + 1.0 look similar)

precision mediump float;
precision mediump int;

uniform sampler2D u_texture;
varying vec4 v_color;
varying vec2 v_texCoords;
uniform float u_smoothing;

void main() {
// SDF
float distance = texture2D(u_texture, v_texCoords).a;
float alpha = smoothstep(0.5 - u_smoothing, 0.5 + u_smoothing, distance);
vec4 sdfColor = vec4(v_color.rgb, alpha * v_color.a);

// Regular
vec4 regularColor = v_color * texture2D(u_texture, v_texCoords);

// Making a choice
gl_FragColor = mix(regularColor, sdfColor, step(1.0, v_texCoords.x));
}


It works and looks fine on desktop and most of popular Android devices but not on Oneplus One, Xiaomi Redmi 4A and Moto G devices. This is how it must look like and how it actually looks on desktops and many other devices (Samsung S8, Xiaomi 3s, Pixel 2 etc.):

And this is how it looks like on Moto G, Oneplus One & some others:

I tried to find a problematic function, but without success. First I tried to use the usual condition instead of mix():

void main() {
// ...

// Making a choice
// gl_FragColor = mix(regularColor, sdfColor, step(1.0, v_texCoords.x));
if (v_texCoords.x < 1.0) {
gl_FragColor = regularColor;
} else {
gl_FragColor = sdfColor;
}
}


Nothing has changed - this shader works well on most devices and is still bad on some others. I decided that the problem lies in the calculation of sdfColor and changed fragment shader code a bit to check:

void main() {
// ...

// Making a choice
gl_FragColor = sdfColor;  // Always using SDF
}


But this shader worked equally on all devices, which indicates that sdfColor is calculated correctly. This next screenshot was taken on Moto G:

Then I tried to differentiate the regions of textures with different colors:

void main() {
// ...

// Making a choice
if (v_texCoords.x < 1.0) {
gl_FragColor = regularColor;
gl_FragColor.r = 1.0;  // Full red channel for regular texture regions without SDF (UV.x < 1.0)
} else {
gl_FragColor = sdfColor;
gl_FragColor.r = 0.0;  // No red channel for SDF
}
}


On Moto G and other devices it looks as expected:

It seems to me that using mix() or conditions implicitly changes something else.

Update 1:

Using different precision qualifier (highp / lowp) or completely removing it didn't help and made no visual changes.

This bug starts to appear on desktop (OpenGL) if I add #version 100. Changing it to #version 110 or higher returns normal rendering.

When #version 100 is used on Android (OpenGL ES 2.0) it renders as if there were no #version qualifier at all (exactly the same as described in my question - on Nexus 5x it looks good, on Moto G it doesn't).

Using #version 300 es on Android (OpenGL ES 3.0, looks like it is supported Moto G 3rd) with respective changes to the code (uniform, varying, texture2D replaced by in, out, texture) doesn't fix the problem (good on Nexus, bad on Moto).

Precision qualifier was not used in both cases. Looks like #version does no effect to this problem on Android but makes it appear on desktop when set to 100 (is it OpenGL 1.0?...)

Update 2:

Writing v_texCoords.x to the separate float and using it in mix() fixes this bug but only for desktop when #version 100 is also used:

#version 100
uniform sampler2D u_texture;
varying vec4 v_color;
varying vec2 v_texCoords;
uniform float u_smoothing;

void main() {
float x = v_texCoords.x; // Writing to the separate float

// SDF
float distance = texture2D(u_texture, v_texCoords).a;
float alpha = smoothstep(0.5 - u_smoothing, 0.5 + u_smoothing, distance);
vec4 sdfColor = vec4(v_color.rgb, alpha * v_color.a);

// Regular
vec4 regularColor = v_color * texture2D(u_texture, v_texCoords);

// Making a choice
gl_FragColor = mix(regularColor, sdfColor, step(1.0, x)); // Using x instead of v_texCoords.x
}


It doesn't fix the problem on Moto G. Also when I move float x = v_texCoords.x; to the line right before gl_FragColor = ... the problem remains on desktop:

void main() {
// SDF
float distance = texture2D(u_texture, v_texCoords).a;
float alpha = smoothstep(0.5 - u_smoothing, 0.5 + u_smoothing, distance);
vec4 sdfColor = vec4(v_color.rgb, alpha * v_color.a);

// Regular
vec4 regularColor = v_color * texture2D(u_texture, v_texCoords);

// Making a choice
float x = v_texCoords.x; // Writing to the separate float, this time it doesn't fix the bug
gl_FragColor = mix(regularColor, sdfColor, step(1.0, x)); // Using x instead of v_texCoords.x
}

• I always calculated sdfColor in the same way. I did not replace it with anything else because it seemed to me that sdfColor is calculated correctly (when I used only sdfColor it worked well) – therainycat Jun 13 '18 at 9:41
• No, replacing mix() with condition doesn't fix the problem, it behaves exactly as with mix() – therainycat Jun 13 '18 at 9:50
• Oops, my bad, I have been misunderstanding the problem, sorry for wasting your time. – gan_ Jun 13 '18 at 9:51
• Do you actually have all those devices how did you manage to test this? Also you might want to try vulkan and see if you get the same issues. The shader wouldn't have to change, but the driver will be consuming SPIR-V instead of the actual shader code text, so its possible that there are driver implementation differences for EGL on those devices that cause this issue, if it worked the same everywhere on vulkan it would hint a problem with the implementation of EGL on those devices, or you are using undefined behavior. – whn Jun 13 '18 at 15:24
• also try using highp instead of mediump, or get rid of the precision qualifiers as recommended for more modern versions of OpenGL ES. Try lowp and see if you see any noticable difference as well and try doing the same for int. Maybe try making the code even simpler for the compiler by always assigning first before using members, its possible the GLSL compiler is bugged on those systems. Also you should probably specify the GLSL version with #version. – whn Jun 13 '18 at 15:34