Loading any glTF model causes crash on Samsung Galaxy S6 edge

1. A concise explanation of the problem you’re experiencing.

Loading any GLTF model causes immediate crash on Samsung Galaxy S6 edge. This includes both glTF, binary glTF and glTF 2.0 models, textured or untextured;

2. A minimal code example. If you’ve found a bug, this helps us reproduce and repair it.

Easiest way to reproduce is to open ‘3D models’ example in sandcastle - this causes immediate crash (while other examples without loaded models work perfectly fine)

3. Context. Why do you need to do this? We might know a better way to accomplish your goal.

4. The Cesium version you’re using, your operating system and browser.

Cesium 1.36 or higher, Samsung Galaxy S6 edge with latest Chrome, Firefox or Android Webview. Probably Galaxy S6 is affected as well

So far I managed to narrow down that this crash was introduced in Cesium v1.36, and in that release glTF 2.0 was introduced.

I noticed that Samsung Galaxy S7/S7edge doesn’t have any similar problems - so I’ve run Khronos WebGL conformance test on both devices failed on the same 4 tests. They also report same results on WebGLReport page.

Attached screenshots have stacktraces from Cesium 1.43 unminified.

Screenshot 2018-03-07 13.48.33.png

Screenshot 2018-03-07 13.48.25.png

Would you be able to run your app using the developer build of Cesium? In the download package this is Cesium-1.43/Build/CesiumUnminified/Cesium.js

That will hopefully produce a more useful error message, including the line number of the shader code that failed.

Stacktrace in my original post is from unminified version and I am not getting an actual error message - it is empty (see attached screenshot). The best I can get is fragment shader source code itself - here it goes:

#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif

uniform vec2 czm_currentFrustum;
const float czm_sceneMode2D = 2.0;

uniform float czm_sceneMode;
uniform vec4 czm_frustumPlanes;
uniform vec4 czm_viewport;
uniform mat4 czm_inverseProjection;
uniform mat4 czm_viewportTransformation;
float czm_metersPerPixel(vec4 positionEC)
{
float width = czm_viewport.z;
float height = czm_viewport.w;
float pixelWidth;
float pixelHeight;
float top = czm_frustumPlanes.x;
float bottom = czm_frustumPlanes.y;
float left = czm_frustumPlanes.z;
float right = czm_frustumPlanes.w;
if (czm_sceneMode == czm_sceneMode2D)
{
float frustumWidth = right - left;
float frustumHeight = top - bottom;
pixelWidth = frustumWidth / width;
pixelHeight = frustumHeight / height;
}
else
{
float distanceToPixel = -positionEC.z;
float inverseNear = 1.0 / czm_currentFrustum.x;
float tanTheta = top * inverseNear;
pixelHeight = 2.0 * distanceToPixel * tanTheta / height;
tanTheta = right * inverseNear;
pixelWidth = 2.0 * distanceToPixel * tanTheta / width;
}
return max(pixelWidth, pixelHeight);
}

vec4 czm_windowToEyeCoordinates(vec4 fragmentCoordinate)
{
float x = 2.0 * (fragmentCoordinate.x - czm_viewport.x) / czm_viewport.z - 1.0;
float y = 2.0 * (fragmentCoordinate.y - czm_viewport.y) / czm_viewport.w - 1.0;
float z = (fragmentCoordinate.z - czm_viewportTransformation[3][2]) / czm_viewportTransformation[2][2];
vec4 q = vec4(x, y, z, 1.0);
q /= fragmentCoordinate.w;
if (czm_inverseProjection != mat4(0.0)) {
q = czm_inverseProjection * q;
} else {
float top = czm_frustumPlanes.x;
float bottom = czm_frustumPlanes.y;
float left = czm_frustumPlanes.z;
float right = czm_frustumPlanes.w;
float near = czm_currentFrustum.x;
float far = czm_currentFrustum.y;
q.x = (q.x * (right - left) + left + right) * 0.5;
q.y = (q.y * (top - bottom) + bottom + top) * 0.5;
q.z = (q.z * (near - far) - near - far) * 0.5;
q.w = 1.0;
}
return q;
}

const int czm_maxClippingPlanes = 6;

float czm_discardIfClippedWithIntersect(vec4 clippingPlanes[czm_maxClippingPlanes], int clippingPlanesLength)
{
if (clippingPlanesLength > 0)
{
bool clipped = true;
vec4 position = czm_windowToEyeCoordinates(gl_FragCoord);
vec3 clipNormal = vec3(0.0);
vec3 clipPosition = vec3(0.0);
float clipAmount = 0.0;
float pixelWidth = czm_metersPerPixel(position);
for (int i = 0; i < czm_maxClippingPlanes; ++i)
{
if (i == clippingPlanesLength)
{
break;
}
clipNormal = clippingPlanes[i].xyz;
clipPosition = -clippingPlanes[i].w * clipNormal;
float amount = dot(clipNormal, (position.xyz - clipPosition)) / pixelWidth;
clipAmount = max(amount, clipAmount);
clipped = clipped && (amount <= 0.0);
}
if (clipped)
{
discard;
}
return clipAmount;
}
return 0.0;
}

float czm_discardIfClippedWithUnion(vec4 clippingPlanes[czm_maxClippingPlanes], int clippingPlanesLength)
{
if (clippingPlanesLength > 0)
{
vec4 position = czm_windowToEyeCoordinates(gl_FragCoord);
vec3 clipNormal = vec3(0.0);
vec3 clipPosition = vec3(0.0);
float clipAmount = 0.0;
float pixelWidth = czm_metersPerPixel(position);
for (int i = 0; i < czm_maxClippingPlanes; ++i)
{
if (i == clippingPlanesLength)
{
break;
}
clipNormal = clippingPlanes[i].xyz;
clipPosition = -clippingPlanes[i].w * clipNormal;
float amount = dot(clipNormal, (position.xyz - clipPosition)) / pixelWidth;
clipAmount = max(amount, clipAmount);
if (amount <= 0.0)
{
discard;
}
}
return clipAmount;
}
return 0.0;
}

#line 0

#line 0

varying vec3 v_normal;
uniform vec4 u_ambient;
uniform vec4 u_diffuse;
uniform vec4 u_emission;
uniform vec4 u_specular;
uniform float u_shininess;
void gltf_blend_main() {
vec3 normal = normalize(v_normal);
vec4 color = vec4(0., 0., 0., 0.);
vec4 diffuse = vec4(0., 0., 0., 1.);
vec4 emission;
vec4 ambient;
vec4 specular;
ambient = u_ambient;
diffuse = u_diffuse;
emission = u_emission;
specular = u_specular;
diffuse.xyz *= max(dot(normal,vec3(0.,0.,1.)), 0.);
color.xyz += diffuse.xyz;
color.xyz += emission.xyz;
color = vec4(color.rgb * diffuse.a, diffuse.a);
gl_FragColor = color;
}
uniform vec4 gltf_color;
uniform float gltf_colorBlend;
void gltf_clip_main()
{
gltf_blend_main();
float alpha = 1.0 - ceil(gl_FragColor.a) + gl_FragColor.a;
gl_FragColor.rgb /= alpha;
gl_FragColor.rgb = mix(gl_FragColor.rgb, gltf_color.rgb, gltf_colorBlend);
float highlight = ceil(gltf_colorBlend);
gl_FragColor.rgb *= mix(gltf_color.rgb, vec3(1.0), highlight);
gl_FragColor.a *= gltf_color.a;
}
uniform int gltf_clippingPlanesLength;
uniform bool gltf_clippingPlanesUnionRegions;
uniform vec4 gltf_clippingPlanes[czm_maxClippingPlanes];
uniform vec4 gltf_clippingPlanesEdgeStyle;
void main()
{
gltf_clip_main();
if (gltf_clippingPlanesLength > 0)
{
float clipDistance;
if (gltf_clippingPlanesUnionRegions)
{
clipDistance = czm_discardIfClippedWithUnion(gltf_clippingPlanes, gltf_clippingPlanesLength);
}
else
{
clipDistance = czm_discardIfClippedWithIntersect(gltf_clippingPlanes, gltf_clippingPlanesLength);
}

    vec4 clippingPlanesEdgeColor = vec4(1.0);
    clippingPlanesEdgeColor.rgb = gltf_clippingPlanesEdgeStyle.rgb;
    float clippingPlanesEdgeWidth = gltf_clippingPlanesEdgeStyle.a;
    if (clipDistance > 0.0 && clipDistance < clippingPlanesEdgeWidth)
    {
        gl_FragColor = clippingPlanesEdgeColor;
    }
}

}

``

Sorry to bother you again, but could you also try running it on master and then pasting the shader source? We recently merged some code that simplifies model shader generation. Hopefully that may isolate where the problem is occurring.

As it stands though the shader text looks ok.

So I used master version of Cesium and got weird results:

If I load model in my application or sandcastle, I don’t get shader compilation error anymore. Instead, I get “context lost” warning and “DeveloperError: drawingBufferWidth must be greater than zero” (see first screenshot).

However, If I try to run “Scene/Model” tests, I get all 214 tests to fail with shader compilation fail (see second screenshot). Compilation log is again empty, but shader source is different:

#ifdef GL_FRAGMENT_PRECISION_HIGH

precision highp float;

#else

precision mediump float;

#endif

#line 0

#line 0

varying vec3 v_normal;

uniform vec4 u_ambient;

uniform vec4 u_diffuse;

uniform vec4 u_emission;

uniform vec4 u_specular;

uniform float u_shininess;

void main(void) {

vec3 normal = normalize(v_normal);

vec4 color = vec4(0., 0., 0., 0.);

vec4 diffuse = vec4(0., 0., 0., 1.);

vec4 emission;

vec4 ambient;

vec4 specular;

ambient = u_ambient;

diffuse = u_diffuse;

emission = u_emission;

specular = u_specular;

diffuse.xyz *= max(dot(normal,vec3(0.,0.,1.)), 0.);

color.xyz += diffuse.xyz;

color.xyz += emission.xyz;

color = vec4(color.rgb * diffuse.a, diffuse.a);

gl_FragColor = color;

}

``

Hope than makes any sense.

It seems like something is causing the WebGL context to crash, which may inadvertently trigger a shader compilation error. The drawingBufferWidth being reported as zero is a common sign that the context has crashed.

Since this only started happening after gltf 2.0 has introduced, I wonder if this has to do with the brdf lookup texture. Can you comment out the line:

frameState.brdfLutGenerator.update(frameState);

``

and run Sandcastle with a glTF 1.0 model?

I can confirm that commenting that line stops cesium from crash and model loads and works fine.

Cool well at least we’ve narrowed it down a bit. I opened an issue here https://github.com/AnalyticalGraphicsInc/cesium/issues/6334. Hopefully I will be able to get an S6 to debug with.