0

I have reports from my users about issues with rendering of half-floats data on certain devices with Mali GPUs (Huawei Honor 9 and Samsung Galaxy S10+ w/ Mali G71 and G76 respectively).

It results in garbled rendering on these devices while works correctly on Adreno and PowerVR GPUs.

I've double checked code and it seems to be correct:

...
     if (model.hasHalfFloats()) {
         GLES20.glVertexAttribPointer(shaderOutline.getRm_Vertex(), 3, GLES20.GL_FLOAT, false, 18, 0);
         GLES20.glVertexAttribPointer(shaderOutline.getRm_Normal(), 3, getGL_HALF_FLOAT(), false, 18, 12);
     } else {
         GLES20.glVertexAttribPointer(shaderOutline.getRm_Vertex(), 3, GLES20.GL_FLOAT, false, 24, 0);
         GLES20.glVertexAttribPointer(shaderOutline.getRm_Normal(), 3, GLES20.GL_FLOAT, false, 24, 12);
     }
...

/**
 * Returns either OES extension for GL 16-bit floats (if used in ES 2.0) or ES 3.0 constant.
 */
protected int getGL_HALF_FLOAT() {
    if(isES3()) {
        return GLES30.GL_HALF_FLOAT;
    } else {
        return GL_HALF_FLOAT_OES;
    }
}

Code seems to correctly detect OpenGL ES 3 and use GLES30.GL_HALF_FLOAT value in getGL_HALF_FLOAT().

Sample shader code:

    vertexShaderCode = "attribute vec4 rm_Vertex;\r\n" + 
            "attribute mediump vec3 rm_Normal;\r\n" +
            "uniform mat4 view_proj_matrix;\r\n" + 
            "uniform float uThickness1;\r\n" + 
            "void main( void )\r\n" + 
            "{\r\n" + 
            "   vec4 pos = vec4(rm_Vertex.xyz, 1.0);\r\n" + 
            "   float dist = (view_proj_matrix * pos).w;\r\n" + 
            "   vec4 normal = vec4(rm_Normal, 0.0);\r\n" + 
            "   pos += normal * uThickness1 * dist;\r\n" + 
            "   gl_Position = view_proj_matrix * pos;\r\n" + 
            "}";

    fragmentShaderCode = "precision mediump float;\r\n" + 
            "uniform vec4 uColor;\r\n" + 
            "void main( void )\r\n" + 
            "{\r\n" + 
            "    gl_FragColor = uColor;\r\n" + 
            "}";
keaukraine
  • 5,315
  • 29
  • 54

1 Answers1

1

I think you have an alignment problem. From this snippet (and your vertex shader):

GLES20.glVertexAttribPointer(shaderOutline.getRm_Normal(), 3, getGL_HALF_FLOAT(), false, 18, 12);

I can infer that you're attempting a vertex structure like so:

float fPos[3];
half fNormal[3];

You have come up with a vertex stride of 18 which has presumably been arrived at by adding up the individual sizes of the elements (3*sizeof(float))+(3*sizeof(half)) = 12 + 6 = 18.

However, the stride should be 20 because otherwise your vertices are misaligned. The 4-byte floats must start on a 4-byte boundary, but that is not the case.

From the GLES3 spec:

Clients must align data elements consistently with the requirements of the client platform, with an additional base-level requirement that an offset within a buffer to a datum comprising N basic machine units be a multiple of N

Columbo
  • 6,648
  • 4
  • 19
  • 30
  • I've edited my code excerpt in question to showcase stride sizes. Alignment mismatch is a quite severe error and will corrupt output on any GPU. However, it works on Adreno and PowerVR GPUs. – keaukraine Mar 10 '19 at 15:17
  • My point is that 18 is never a valid stride for a vertex that includes a float, because 18 is not a multiple of 4. – Columbo Mar 10 '19 at 15:27
  • How can I retrieve this hardware-specific alignment value? – keaukraine Mar 10 '19 at 17:20
  • 2
    I quoted the relevant part of the spec - a float comprises 4 machine units (bytes), so it needs to be aligned to 4 byte boundaries - it's not hardware specific. Just because code works on some platforms doesn't mean it's correct. People often think the worst thing about undefined behaviour is that it could crash or cause major bugs, but often the worst thing about it is that it can work absolutely fine - until it doesn't. – Columbo Mar 10 '19 at 19:16
  • 2
    The general recommendation for this is to always pack vectors of half floats to be an even number of items, packing a scalar and a `vec3` together to make a `vec4` attribute if possible to avoid padding bytes. – solidpixel Mar 10 '19 at 19:23