3

I've got a distance field shader that I use for font rendering in LibGDX. It takes a uniform that sets how bold the text should be. All this has been working fine for ages, but in the last week or so after a recent Galaxy S6 update that seems to be rolling out (in the US I believe) I've had several reports of incorrect font rendering. (If you have an S6 and want to see the problem you can download here)

My font rendering involves 3 passes, once for a drop shadow, once for a stroke and once for the main text. The drop shadow and stroke are drawn bolder than the main text (depending on font settings)

The problem I'm having is the S6 would appear to be ignoring me changing the uniform to make the text less bold, so it's drawing the text too bold and merging the letters into each other.

Below is an example of incorrect and correct rendering (No drop shadow in this).

Example

The game has been out for over a year and installed on over 500k devices and this problem has only just started occurring.

I don't have a problematic S6 to test on which makes it tricky. Here's my font rendering method.

private GlyphLayout drawText(float x, float y, CharSequence str, float alignmentWidth, int alignment, boolean wrap, SpriteBatch drawBatch) {
    if (dropShadowColour.a > 0) {
        font.setColor(dropShadowColour.r, dropShadowColour.g, dropShadowColour.b, dropShadowColour.a * originalColour.a);
        font.draw(drawBatch, str,
                x + font.getCapHeight() * shadowOffset.x,
                y + font.getCapHeight() * shadowOffset.y,
                alignmentWidth, alignment, wrap);
        font.setColor(originalColour);
    }

    if (strokeSize != 0) {
        font.setColor(strokeColour);
        font.draw(drawBatch, str, x, y, alignmentWidth, alignment, wrap);
        drawBatch.flush();
        drawBatch.getShader().setUniformf("u_boldness", 0);
    }

    font.setColor(originalColour);
    return font.draw(drawBatch, str, x, y, alignmentWidth, alignment, wrap);
}

And the fragment shader

uniform sampler2D u_texture;
uniform float u_boldness;
uniform float u_smoothing;

varying vec4 v_color;
varying vec2 v_texCoord;

void main()
{
    float distance = texture2D(u_texture, v_texCoord).b;
    float alpha = smoothstep(0.5 - u_boldness - u_smoothing, 0.5 - u_boldness + u_smoothing, distance);
    gl_FragColor = vec4(v_color.rgb * alpha, alpha * v_color.a);
}

Is there anything else I should be doing when setting the uniform? I'm not expected to begin and end the shader am I? Any pointers would be useful.

Update If I call drawBatch.end(); drawBatch.begin(); instead of drawBatch.flush(); then the issue is resolved. This however, is not efficient, so I'd like a better solution.

Another Update

I use this to get around the issue for now

public static void safeFlush(SpriteBatch spriteBatch){
    spriteBatch.flush();
    spriteBatch.getShader().end();
    spriteBatch.getShader().begin();
}
Will Calderwood
  • 4,393
  • 3
  • 39
  • 64
  • Try `shaderProgram.getLog()` after the shader is loaded to see if there are any warnings. Also, maybe use `.r` or `.a` instead of `.b` in the first line of your shader (if this is a grayscale png). – Tenfour04 Jul 07 '15 at 18:36
  • @Tenfour04 I check the shader has compiled and log errors, although don't log warnings. If there is anything returned from getLog but no error I assume that would be a warning? If so, I'll log that. I know the uniform is being set initially, it's just not being reset to zero. I use the r & g channels for a gradient. Not having a test device makes this a difficult one to debug. Fortunately I've got a helpful user. – Will Calderwood Jul 07 '15 at 19:46
  • I think so. Errors will cause the shader not to compile at all. With warnings, sometimes there's no issue, but on some devices it could compile but look wrong. Also I've found that what works on desktop sometimes doesn't work on mobile. I think WebGL is a lot less pendantic about shader syntax than many mobile GPUs are. I've also had an issue before with shaders looking wrong on Samsung devices and was lucky enough to have a nice user who wanted to test for me. – Tenfour04 Jul 07 '15 at 19:50
  • @Tenfour04 In the version I sent to me test user yesterday I tried calling end and begin on the sprite batch instead of flush. This has resolved the issue, although inefficient. I'll add logging of warnings in the next version. – Will Calderwood Jul 08 '15 at 05:40
  • Thanks for reporting - I'll raise this with the Mali Driver team and post back if we find out what the issue is. – solidpixel Jul 08 '15 at 08:46
  • P.S. Would it be possible to get some additional info on what the failing OpenGL ES call sequence looks like? Not sure how drawBatch library calls translate into API calls. – solidpixel Jul 08 '15 at 08:53
  • @Isogen74 I'll see what I can establish. While I've got your attention, please can you also get your driver team to sort out the bug on low end Samsung devices mentioned [here](http://stackoverflow.com/questions/10871261/java-lang-runtimeexception-eglswapbuffers-failed-egl-success-report). It only occurs when using textures > 1024x1024 and has existed for years and only occurs on Samsung devices. – Will Calderwood Jul 08 '15 at 09:03
  • 1
    @Isogen74 Probably easiest if you look at the [SpriteBatch code](https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/SpriteBatch.java). Updating the uniform after calling `flush()` doesn't work. Updating it after calling `end();begin()` does. – Will Calderwood Jul 08 '15 at 09:15
  • 1
    @Isogen74 In terms of GL calls, the difference between calling `end();begin()` and `flush()` in LibGDX is that `end();begin()` results in also calling `gl.glUseProgram(0);gl.glUseProgram(myShader);`, so it would seem that the Mali requires the shader to be set again before uniforms are changed. – Tenfour04 Jul 08 '15 at 13:00
  • Could it be so that the u_boldness uniform remains 0 when you're calling font.draw in the end? So it ends up covering the shadow and the outline. – rgngl Jul 08 '15 at 14:01
  • @rgngl When it's set to zero there's a space between the characters as in the correct image. Image that without the outline. That's `u_boldness==0` – Will Calderwood Jul 08 '15 at 15:36
  • @WillCalderwood I mean, you first draw it with u_boldness set to zero, then you draw the supposedly normal text, but the shader will preserve the values of uniforms. So when you're drawing the insides of the text, you're actually drawing with u_boldness==0. Am I wrong? – rgngl Jul 08 '15 at 19:17
  • @rgngl u_boldness is not set to zero for the initial draw. u_boldness is set to the stroke size before drawText is called. It's then is set to zero for the final draw so that the letters don't overlap. On the S6 the call to `drawBatch.getShader().setUniformf("u_boldness", 0);` is doing nothing. – Will Calderwood Jul 08 '15 at 21:49
  • @Isogen74 Just to confirm, calling `gl.glUseProgram(0);gl.glUseProgram(myShader);` does resolve the issue. – Will Calderwood Jul 09 '15 at 09:14
  • @WillCalderwood Thanks - useful information. Is there any specific part of the application where it goes wrong, or is all of the text rendering wrong? – solidpixel Jul 10 '15 at 08:03
  • @Isogen74 All of the text rendering is wrong. It appears although the uniform can't be set after the initial draw. As I don't have an S6 it's very difficult for me to tell exactly what's going on. I've worked around this for now by resetting the shader program on each flush on the S6. If you need the old APK to do any testing with your end then give me a shout. – Will Calderwood Jul 10 '15 at 08:17
  • @WillCalderwood That would be very useful thanks - can you create a post in the Mali Graphics area of http://community.arm.com/groups/arm-mali-graphics and I'll get one of our Mali ecosystem developer team to get in touch. – solidpixel Jul 10 '15 at 08:48

0 Answers0