Android speed vs...well...everything

Monkey Targets Forums/Android/Android speed vs...well...everything

Ken(Posted 2013) [#1]
Hi All,

I just got around to trying a silly little test on Android to see what the speed of the JVM is like (code at the bottom). It's the warpy->bezier example, but with the control points bouncing around the screen.

I've been using MonkeyPro70b up to now, but on OSX that's broken...the compiles die with a missing symbol. So for Android I went back to 67f.

In all other environments that I've tried (HTML5, iOS (both on iPad 2 and an old iPod), GLFW), it just goes like stink. Easily handles 60 frames per second, and I can click to add more bezier's all day long, and it never seems to slow down before I get tired of adding them.

On Android? Running on a Nexus 4, I get about 5 beziers on the screen, and I notice degraded performance. Put 10 on, and you feel it. 20, and it looks like it's skipping frames. More than that, and we're heading towards seconds per frame rather than frames per second.

On a Nexus 4!?!?

Let that run for a little while and the back of the phone gets hot!

Is there something inherently evil about this code that craters Android? Is there a less naive way to draw bezier curves more quickly on Android? Is there a native client on Android for Monkey?

-Ken




muddy_shoes(Posted 2013) [#2]
The code is rendering a shed-load of lines. Over 250 per bezier when I tried it. The simplest thing would be to reduce that number by changing the increment on the bezier. In my own curve rendering I subdivide the curve and stop when the mid-point of a straight line approximation is within a given distance of the actual curve point.

Beyond simply dropping the accuracy there are a bunch of things that could be done to speed things up but the main one is probably to alter mojo to have a DrawLineList function to batch the render calls and avoid the overhead of repeatedly calling its internal Begin method.

Whether that's worth doing or not depends on whether this is more than just a "little test".


Ken(Posted 2013) [#3]
I'm building an app that will need to be able to have up to 100 bezier curves on screen and live at any one time. I'd like to be able to animate the curves, so I wanted to see maximum speed.

I was just surprised that Android, at least in this test, seemed to be so much slower than on other devices.

That said, for my purposes the curves don't need to be perfect by any stretch (and in fact the fewer points on a bezier the better), so if I limit the number of sub points on a line to 10 (maybe 20) that seems to do fine for the look of the bezier.

Even better, though I like the buttery look of an update rate of 60, it's not even close to necessary. I was exaggerating everything to find where the boundaries are. If I drop that to 30, I'm easily within the requirements of my app.

Your post made me curious about the internals of Mojo...I've just been using Mojo as a black box up to now.

Wow. Just looked at the low level implementation of DrawLine in the native code for Mojo. I /think/ I can see how DrawLine could be extended to be, say, DrawMultiLine(verts#[]), and so the Begin would only get called once (for up to roughly 1600 (or is it 3200?) points)...but I'm a complete OpenGL newbie.

Some platforms make it obvious when the line appears (in .js lineTo(x,y)). but in the case of the raw OpenGL calls, it seems to be adding vertices, but I don't see where those are actually drawn.

It's *cool* to be able to see the source of this stuff!

-Ken


muddy_shoes(Posted 2013) [#4]
The actual drawing occurs behind the scenes when the vertex data is sent to the GL driver/hardware. The hand-off occurs in the Flush call. Here's a suitable method for the java implementation:

//Expects an array of floats describing the vertex data for a
//continuous set of connected line segments with no duplicated vertices.
//The vertices contain x,y,u,v data, with the u and v components being
//ignored for lines. So a line from 50.0,100.0 to 40.0,30.0 would be in an
//8 element array - [50.0, 100.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0]

int DrawLineStrip( float[] verts){
        Begin( GLES11.GL_LINE_STRIP, verts.length/4, null );
		
        java.lang.System.arraycopy(verts,0,vertices,vp,verts.length);
        java.util.Arrays.fill(colors,cp,cp+verts.length/4,colorARGB);
		
        if( tformed ){
            for( int i=vp+verts.length;vp<i;vp+=4 ){
                float x = vertices[vp];
                float y = vertices[vp+1];
                vertices[vp] = x * ix + y * jx + tx;
                vertices[vp+1] = x * iy + y * jy + ty;
	    }
	}
	cp+=verts.length/4;
        Flush();
 	return 0;
}


You could change it to take plain x,y pairs, but at the cost of having to copy all the data over at greater expense than the system call. You could also further alter mojo to avoid that issue at the cost of increasing effort and future pain when updating Monkey.

In addition to this you'll also have to add the references/functions to graphicsdevice.monkey and graphics.monkey.


muddy_shoes(Posted 2013) [#5]
Here's the draw method altered to use the line strip call. I've micro-optimised a bit by inlining the x and y position calculations and removing the redundant calcs.

Const BEZIER_STEPS:Int = 50 
Field verts:Float[] = New Float[ 4*(BEZIER_STEPS+1) ]
	
Method draw()
	
    Self.inc = 1.0 / BEZIER_STEPS

    'draw the control points
    SetColor 0,0,255
    DrawPoints points
    'draw lines showing how the second and third control points define the direction of the curve
    SetAlpha .3
    DrawLine points[0],points[1],points[2],points[3]
    DrawLine points[4],points[5],points[6],points[7]
    SetAlpha 1
		
    'draw the curve
    'it will work by increasing t from 0 to 1, 
    'calculating the co-ordinates of the corresponding point on the curve at each step
    SetColor 255,255,255
		
    verts[0] = points[0]
    verts[1] = points[1]
		
    Local vertIndex:Int = 4
    Local vertCount:Int = verts.Length()
			
    Local x#,y#
    Local t#=0
		
    While vertIndex < vertCount
        t+=inc	'add inc to t, so moving along the curve a little bit
        If t>1.0 t=1.0
			
        'calculate the position of the corresponding point on the curve
        Local nt:Float = 1.0-t
        Local nt3:Float = nt*nt*nt
        Local nttx3:Float = 3*nt*t
        Local t3:Float = t*t*t
			
        x = nt3*points[0] + nt*nttx3*points[2] + nttx3*t*points[4] + t3*points[6]
        y = nt3*points[1] + nt*nttx3*points[3] + nttx3*t*points[5] + t3*points[7]
	
        'copy point to vert array
        verts[vertIndex] = x
        verts[vertIndex+1] = y
        vertIndex +=4

    End
			
    DrawLineStrip( verts )
End