Проблемы с HALF_FLOAT на графических процессорах Mali

#android #opengl-es #opengl-es-3.0 #mali

#Android #opengl-es #opengl-es-3.0 #mali

Вопрос:

У меня есть сообщения от моих пользователей о проблемах с отображением данных в режиме полуплавания на некоторых устройствах с графическими процессорами Mali (Huawei Honor 9 и Samsung Galaxy S10 с Mali G71 и G76 соответственно).

Это приводит к искаженному рендерингу на этих устройствах, хотя корректно работает на графических процессорах Adreno и PowerVR.

Я дважды проверил код, и он кажется правильным:

 ...
     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;
    }
}
  

Кажется, код правильно определяет OpenGL ES 3 и использует GLES30.GL_HALF_FLOAT значение в getGL_HALF_FLOAT() .

Пример кода шейдера:

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

    fragmentShaderCode = "precision mediump float;rn"   
            "uniform vec4 uColor;rn"   
            "void main( void )rn"   
            "{rn"   
            "    gl_FragColor = uColor;rn"   
            "}";
  

Ответ №1:

Я думаю, у вас проблема с выравниванием. Из этого фрагмента (и вашего вершинного шейдера):

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

Я могу сделать вывод, что вы пытаетесь создать структуру вершин следующим образом:

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

Вы получили высоту шага в 18 вершин, которая, предположительно, была получена путем суммирования отдельных размеров элементов (3*sizeof(float)) (3*sizeof(half)) = 12 6 = 18 .

Однако шаг должен быть равен 20, потому что в противном случае ваши вершины будут смещены. 4-байтовые числа с плавающей точкой должны начинаться с 4-байтовой границы, но это не так.

Из спецификации GLES3:

Клиенты должны согласовывать элементы данных в соответствии с требованиями клиентской платформы, с дополнительным требованием базового уровня, чтобы смещение в буфере относительно базы данных, содержащей N базовых машинных блоков, было кратным N

Комментарии:

1. Я отредактировал свой фрагмент кода, о котором идет речь, чтобы продемонстрировать размеры шага. Несоответствие выравнивания является довольно серьезной ошибкой и приведет к повреждению вывода на любом графическом процессоре. Однако это работает на графических процессорах Adreno и PowerVR.

2. Я хочу сказать, что 18 никогда не является допустимым шагом для вершины, которая включает в себя число с плавающей точкой, потому что 18 не кратно 4.

3. Как я могу получить это значение выравнивания, зависящее от оборудования?

4. Я процитировал соответствующую часть спецификации — число с плавающей точкой состоит из 4 машинных единиц (байт), поэтому его необходимо выровнять по границам в 4 байта — это не зависит от аппаратного обеспечения. То, что код работает на некоторых платформах, не означает, что он правильный. Люди часто думают, что самое худшее в неопределенном поведении — это то, что оно может привести к сбою или вызвать серьезные ошибки, но часто самое худшее в этом то, что оно может работать абсолютно нормально — пока этого не произойдет.

5. Общая рекомендация для этого заключается в том, чтобы всегда упаковывать векторы с половинными плавающими значениями в четное количество элементов, по возможности упаковывая скаляр и vec3 вместе, чтобы создать vec4 атрибут, чтобы избежать заполнения байтов.