My App works fine on Emulator and all-test devices but not with PowerVR SGX544 powered devices

I have accurately tested my app before release it, on Emulator set with different screen size (and with different Android SDK and CPU emulations), and many real devices. No problems, everything works fine. Now an user has reported a bug with his tablet.



I’m testing the app on tons of devices and the issue happens only if the devices use some kind of soc ARM with PowerVR SGX544(and maybe other similar GPUs) and Android 4.x.



The app doesn’t use any texture, only GL11, GL10 and GLView to plot some graph, and runs smooth also on old cheap smartphones with at least Gingerbread… but with this Power VR the result of the plot is an unreadable and laggy graphic glitch



No error in the Eclipse logs, No crash or code deprecation warning





The code of the section with the bug (I cannot be more syntetic because I get no error)

class Graph3d {<br />
private final int N = 48;<br />
private ShortBuffer verticeIdx;<br />
private FloatBuffer vertexBuf;<br />
private ByteBuffer colorBuf;<br />
private int vertexVbo, colorVbo, vertexElementVbo;<br />
private boolean useVBO;<br />
private int nVertex;<br />
<br />
Graph3d(GL11 gl) {<br />
short[] b = new short[N*N];<br />
int p = 0;<br />
for (int i = 0; i < N; i++) {<br />
short v = 0;<br />
for (int j = 0; j < N; v += N+N, j+=2) {<br />
b[p++] = (short)(v+i);<br />
b[p++] = (short)(v+N+N-1-i);<br />
}<br />
v = (short) (N*(N-2));<br />
i++;<br />
for (int j = N-1; j >= 0; v -= N+N, j-=2) {<br />
b[p++] = (short)(v+N+N-1-i);<br />
b[p++] = (short)(v+i);<br />
}<br />
}<br />
verticeIdx = buildBuffer(b);<br />
<br />
String extensions = gl.glGetString(GL10.GL_EXTENSIONS);<br />
useVBO = extensions.indexOf("vertex_buffer_object") != -1;<br />
Calculator.log("VBOs support: " + useVBO + " version " + gl.glGetString(GL10.GL_VERSION));<br />
<br />
if (useVBO) {<br />
int[] out = new int[3];<br />
gl.glGenBuffers(3, out, 0);<br />
vertexVbo = out[0];<br />
colorVbo  = out[1];<br />
vertexElementVbo = out[2];<br />
}<br />
}<br />
<br />
private static FloatBuffer buildBuffer(float[] b) {<br />
ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 2);<br />
bb.order(ByteOrder.nativeOrder());<br />
FloatBuffer sb = bb.asFloatBuffer();<br />
sb.put(b);<br />
sb.position(0);<br />
return sb;<br />
}<br />
<br />
private static ShortBuffer buildBuffer(short[] b) {<br />
ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 1);<br />
bb.order(ByteOrder.nativeOrder());<br />
ShortBuffer sb = bb.asShortBuffer();<br />
sb.put(b);<br />
sb.position(0);<br />
return sb;<br />
}<br />
<br />
private static ByteBuffer buildBuffer(byte[] b) {<br />
ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 1);<br />
bb.order(ByteOrder.nativeOrder());<br />
bb.put(b);<br />
bb.position(0);<br />
return bb;<br />
}<br />
<br />
public void update(GL11 gl, Function f, float zoom) {<br />
final int NTICK = Calculator.useHighQuality3d ? 5 : 0;<br />
final float size = 4*zoom;<br />
final float minX = -size, maxX = size, minY = -size, maxY = size;<br />
<br />
Calculator.log("update VBOs " + vertexVbo + ' ' + colorVbo + ' ' + vertexElementVbo);<br />
nVertex = N*N+6+8 + NTICK*6;<br />
int nFloats = nVertex * 3;<br />
float vertices[] = new float[nFloats];<br />
byte colors[] = new byte[nVertex << 2];<br />
if (f != null) {<br />
Calculator.log("Graph3d update");<br />
float sizeX = maxX - minX;<br />
float sizeY = maxY - minY;<br />
float stepX = sizeX / (N-1);<br />
float stepY = sizeY / (N-1);<br />
int pos = 0;<br />
double sum = 0;<br />
float y = minY;<br />
float x = minX - stepX;<br />
int nRealPoints = 0;<br />
for (int i = 0; i < N; i++, y+=stepY) {<br />
float xinc = (i & 1) == 0 ? stepX : -stepX;<br />
x += xinc;<br />
for (int j = 0; j < N; ++j, x+=xinc, pos+=3) {<br />
float z = (float) f.eval(x, y);<br />
vertices[pos] = x;<br />
vertices[pos+1] = y;<br />
vertices[pos+2] = z;<br />
if (z == z) { // not NAN<br />
sum += z * z;<br />
++nRealPoints;<br />
}<br />
}<br />
}<br />
float maxAbs = (float) Math.sqrt(sum / nRealPoints);<br />
maxAbs *= .9f;<br />
maxAbs = Math.min(maxAbs, 15);<br />
maxAbs = Math.max(maxAbs, .001f);<br />
<br />
final int limitColor = N*N*4;<br />
for (int i = 0, j = 2; i < limitColor; i+=4, j+=3) {<br />
float z = vertices[j];<br />
if (z == z) {<br />
final float a = z / maxAbs;<br />
final float abs = a < 0 ? -a : a;<br />
colors<i>   = floatToByte(a);<br />
colors[i+1] = floatToByte(1-abs*.3f);<br />
colors[i+2] = floatToByte(-a);<br />
colors[i+3] = (byte) 255;<br />
} else {<br />
vertices[j] = 0;<br />
z = 0;<br />
colors<i>   = 0;<br />
colors[i+1] = 0;<br />
colors[i+2] = 0;<br />
colors[i+3] = 0;<br />
}<br />
}<br />
}<br />
int base = N*N*3;<br />
int colorBase = N*N*4;<br />
int p = base;<br />
final int baseSize = 2;<br />
for (int i = -baseSize; i <= baseSize; i+=2*baseSize) {<br />
vertices[p] = i; vertices[p+1] = -baseSize; vertices[p+2] = 0;<br />
p += 3;<br />
vertices[p] = i; vertices[p+1] = baseSize; vertices[p+2] = 0;<br />
p += 3;<br />
vertices[p] = -baseSize; vertices[p+1] = i; vertices[p+2] = 0;<br />
p += 3;<br />
vertices[p] = baseSize; vertices[p+1] = i; vertices[p+2] = 0;<br />
p += 3;<br />
}<br />
for (int i = colorBase; i < colorBase+8*4; i += 4) {<br />
colors<i> = 0;<br />
colors[i+1] = 0;<br />
colors[i+2] = (byte) 255;<br />
colors[i+3] = (byte) 255;<br />
}<br />
base += 8*3;<br />
colorBase += 8*4;<br />
<br />
final float unit = 2;<br />
final float axis[] = {<br />
0, 0, 0,<br />
unit, 0, 0,<br />
0, 0, 0,<br />
0, unit, 0,<br />
0, 0, 0,<br />
0, 0, unit,<br />
};<br />
System.arraycopy(axis, 0, vertices, base, 6*3);<br />
for (int i = colorBase; i < colorBase+6*4; i+=4) {<br />
colors<i> = (byte) 255;<br />
colors[i+1] = (byte) 255;<br />
colors[i+2] = (byte) 255;<br />
colors[i+3] = (byte) 255;<br />
}<br />
base += 6*3;<br />
colorBase += 6*4;<br />
<br />
p = base;<br />
final float tick = .03f;<br />
final float offset = .01f;<br />
for (int i = 1; i <= NTICK; ++i) {<br />
vertices[p]   = i-tick;<br />
vertices[p+1] = -offset;<br />
vertices[p+2] = -offset;<br />
<br />
vertices[p+3] = i+tick;<br />
vertices[p+4] = offset;<br />
vertices[p+5] = offset;<br />
p += 6;<br />
<br />
vertices[p]   = -offset;<br />
vertices[p+1] = i-tick;<br />
vertices[p+2] = -offset;<br />
<br />
vertices[p+3] = offset;<br />
vertices[p+4] = i+tick;<br />
vertices[p+5] = offset;<br />
p += 6;<br />
<br />
vertices[p]   = -offset;<br />
vertices[p+1] = -offset;<br />
vertices[p+2] = i-tick;<br />
<br />
vertices[p+3] = offset;<br />
vertices[p+4] = offset;<br />
vertices[p+5] = i+tick;<br />
p += 6;<br />
<br />
}<br />
for (int i = colorBase+NTICK*6*4-1; i >= colorBase; --i) {<br />
colors<i> = (byte) 255;<br />
}<br />
<br />
vertexBuf = buildBuffer(vertices);<br />
colorBuf  = buildBuffer(colors);<br />
<br />
if (useVBO) {<br />
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vertexVbo);<br />
gl.glBufferData(GL11.GL_ARRAY_BUFFER, vertexBuf.capacity()*4, vertexBuf, GL11.GL_STATIC_DRAW);<br />
vertexBuf = null;<br />
<br />
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, colorVbo);<br />
gl.glBufferData(GL11.GL_ARRAY_BUFFER, colorBuf.capacity(), colorBuf, GL11.GL_STATIC_DRAW);<br />
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);<br />
colorBuf = null;<br />
<br />
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, vertexElementVbo);<br />
gl.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, verticeIdx.capacity()*2, verticeIdx, GL11.GL_STATIC_DRAW);<br />
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);<br />
}<br />
}<br />
<br />
private byte floatToByte(float v) {<br />
return (byte) (v <= 0 ? 0 : v >= 1 ? 255 : (int)(v*255));<br />
}<br />
<br />
public void draw(GL11 gl) {<br />
if (useVBO) {<br />
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vertexVbo);<br />
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, 0);<br />
<br />
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, colorVbo);<br />
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, 0);<br />
<br />
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);<br />
// gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, N*N);<br />
<br />
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, vertexElementVbo);<br />
gl.glDrawElements(GL10.GL_LINE_STRIP, N*N, GL10.GL_UNSIGNED_SHORT, 0);<br />
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);<br />
} else {<br />
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuf);<br />
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, colorBuf);<br />
gl.glDrawElements(GL10.GL_LINE_STRIP, N*N, GL10.GL_UNSIGNED_SHORT, verticeIdx);<br />
}<br />
final int N2 = N*N;<br />
gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, N2);<br />
gl.glDrawArrays(GL10.GL_LINES, N2, nVertex - N2);<br />
}<br />
}
```<br />
<br />
<br />
<a href="http://stackoverflow.com/questions/18728871/android-opengl-es-issue-only-with-a-specific-gpu-without-any-error-in-logcat">More details HERE</a></i></i></i></i></i>

Hi,



Can you share screenshots of a correct and incorrect render? I’d like to better understand the artefacts you’re seeing.



Also, can you try disabling depth tests? On some SGX544 platforms, we’ve seen depth fighting when co-planar primitives are drawn in 2D renders. You shouldn’t need depth tests for your graphs render anyway.



Thanks,

Joe

Hi,

Can you share screenshots of a correct and incorrect render? I'd like to better understand the artefacts you're seeing.

Also, can you try disabling depth tests? On some SGX544 platforms, we've seen depth fighting when co-planar primitives are drawn in 2D renders. You shouldn't need depth tests for your graphs render anyway.

Thanks,
Joe


Thanks for the answer,

This is what appears on emulator and devices (both smartphone and tablets) with various GPU of Tegra, Adreno and Mali series


The image can be rotated with touch and respond istantly to the touch

Here what I see on PowerVR SGX544 tablet

the result seems freezed and if I try to rotate with touch the graph responds after many seconds and appears similar non-sense glitches


Where (and how) exactly I have to disable depth test?

Hi,



Sorry for the slow response.



Based on the artefacts in your images, I don’t think depth buffer tests are the cause.



On each of your target devices, which ones are using the VBO path? Could there possibly be a flaw in one of your rendering paths?



Can you email devtech@imgtec.com with either a PVRTrace recording of your application or the APK itself so we can investigate? If required, we can give you details for our file sharing system.



Regards,

Joe






Hi,



Can you share the name of the tablet you’re using? Can you also give us the version number of the device’s graphics driver (instructions to query it can be found here). It seems like the bug may be specific to that device.



I’ve ran the app on a Galaxy S 4 (GT-I9500) which contains an SGX544, but the calculation I’d entered (x^2+y^2) is rendering as I’d expected.



Thanks,

Joe

For the test I use Goclever Orion 7o



CPU Soc: AllWinner A31 8CORE GPU POWERVR SGX544MP2 (afaik Galaxy S4 should use the SGX544MP3)

OS: Android 4.2.2 Kernel 3.3.0

Firmware version 1.00_20130605 build JDQ39

SGXDDK: 1.10@2359475

SGX revision = 1.1.5

Hi,



Can you also share the calculation you’re using in your reproduction case? I want to remove the possibility that the calculation I’ve chosen doesn’t hit the problem.



Regards,

Joe

Hi,

Can you also share the calculation you're using in your reproduction case? I want to remove the possibility that the calculation I've chosen doesn't hit the problem.

Regards,
Joe


Happens with every 3D graph calculation. sin(x)cos(y) cos(y)x etc

Hi,



I was able to reproduce artefacts on a tablet with an AllWinner A31 SoC (Onda v972). I’m still not sure what the cause is. I’ll continue investigating the issue later this week.



Regards,

Joe

Hi,

I was able to reproduce artefacts on a tablet with an AllWinner A31 SoC (Onda v972). I'm still not sure what the cause is. I'll continue investigating the issue later this week.

Regards,
Joe


Thanks, I hope you could help me. My knowledge of PowerVR GPU is not enough to find a workaround for this absurd issue

After further investigation, I don’t believe this is a graphics driver or hardware problem.



I used PVRTrace to capture the application running on a device that renders as expected (Galaxy Nexus) and another that reproduces the artefacts you’ve seen (Onda V972). When replaying the OpenGL ES capture in PVRTraceGUI, the Onda recording is still incorrect. If it was a graphics driver issue, then the PVRTraceGUI render shouldn’t contain the artefacts. It seems that the data submitted to OpenGL ES is incorrect.



Have you tried reviewing the contents of the buffers you’re submitting to GL to ensure the values are correct?



Regards,

Joe

After further investigation, I don't believe this is a graphics driver or hardware problem.

I used PVRTrace to capture the application running on a device that renders as expected (Galaxy Nexus) and another that reproduces the artefacts you've seen (Onda V972). When replaying the OpenGL ES capture in PVRTraceGUI, the Onda recording is still incorrect. If it was a graphics driver issue, then the PVRTraceGUI render shouldn't contain the artefacts. It seems that the data submitted to OpenGL ES is incorrect.

Have you tried reviewing the contents of the buffers you're submitting to GL to ensure the values are correct?

Regards,
Joe


I don't know what kind of test should I do. How is possible that the buffer content is wrong only for this GPU?

However I have posted the methods buildBuffer in this thread, (and I have used in each method bb.order(ByteOrder.nativeOrder());). Is there any other thing that should I do to ensure compatibility also to this GPU?

I don’t think it’s a GPU compatibility problem. I suspect that something is causing your buffer to become corrupt/incorrect before it is uploaded to GL. As I mentioned above, using PVRTrace to capture the data your application has submitted to GL (before it’s been sent to the graphics driver) has reproduced the artefacts. When I used PVRTrace to play back a capture I took on a Galaxy Nexus, the render was correct.



It may be the case that a bug was introduced into OS functionality when it was ported to the platform. I would recommend performing the same calculation on two devices (ones that works, one that doesn’t) and comparing the resultant values that are uploaded to GL. This way, you should be able to isolate the cause of the incorrect values.



Regards,

Joe