Low FPS with android SurfaceView - android

I have some trubles with my framerate using a SurfaceView. Im doing the tipical stuff i found in some tutorials (all of them said the same), but i cant reach a decent framerate on my samsung galaxy S (the old one, i9000).
Here's the code i have for the loop thread. FPS is initialized at 30.
#Override
public void run() {
long ticksPS = 1000/FPS;
long startTime;
long sleepTime;
//fps checker
long contms=0;
long lasttimecheck = System.currentTimeMillis();
int fps=0;
while (running) {
long time = System.currentTimeMillis();
if(contms>1000) {
Log.v("FPS",String.valueOf(fps));
contms=time-lasttimecheck;
fps=1;
}
else {
fps++;
contms+=time-lasttimecheck;
}
lasttimecheck = time;
Canvas c = null;
startTime =time;
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime > 10)
sleep(sleepTime);
else {
Log.w("LOWFPS",String.valueOf(contms));
sleep(10);
}
} catch (Exception e) {}
}
}
In the surfaceView, I initialize the holder with holder.setFormat(PixelFormat.RGBA_8888); but i dont know if i have to do something with the bitmaps to avoid useless CPU work (I save the bitmaps in local variables, then I draw them). The game is simple, it should run much faster.
The framerate is quite random, sometimes it works smoothie, sometimes it doesnt, but always under the 30FPS.
Any ideas???
EDIT WITH ONDRAW EXPLANATION
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bg, 0, 0, null); //1
stars.draw(canvas,dx,dy); //2
if(playing.on()) reactors.draw_reaccio(canvas,dx,dy); //3
gotes.draw(canvas,dx,dy); //4
reactors.draw(canvas,dx,dy); //5
sg.draw(canvas); //6
sr.draw(canvas); //7
eraser.draw(canvas); //8
playing.draw(canvas); //9
opcions.draw(canvas); //10
}
1) Drawing the background (480x800)
2) this is a class that contains a list of "getHeight()" basic objects (stars) with its coordenates (x,y) and the ID with the associated image (about 9 different stars images)
3) it draws n*2 circles, one with fill and another with stroke per related object (about 20 or so)
4) It draws the main object of the game, little drops with an animation. There are 9 different kind of drops, and each of them have 5 related images of the animation (should i put the 5 images in 1 maybe?)
5) same as drops but without animation
6 to 10) irrelevant, it just draw an image
I guess the slowness is due to: (2) because of the number of stars (4) becouse of the animation, witch change every 2-3 frames to a different image and maybe it is too much for memory, i guess i should merge all the images in just 1.
The framerate is about 20-22 FPS with S. Galaxy S i9000

If you disable the drawing, how much fps do you achieve ?
Just to check how much the display pipeline is hogging.
I had tried following
create 5 bitmaps of size 720 x 480 : pix format RGB565
display them on a SurfaceView from a loop similar to yours.
The only difference is that I had "prepared bitmaps" and wasn't drawing them in a loop.
This is what was achieved on a Nexus-S phone
FPS : 55 (I tried to run the loop as fast, without regulation)
Cpu load : 85%
This is when I decided to render my SurfaceView from JNI :) !!
Do a similar experiment and see how much your device is able to throttle "without" drawing operations. If it looks good, then you can profile whether you can fit-in your your drawing operations within the budget.

Things to consider : How long does you draw routine take?
If you have problems with it running under 30 FPS, I would reconsider having it sleep. This is not an exact thing and could sleep less or more according to the API, so I would remove this for now and let it run as fast as it can.
I agree with above, please post drawing code or comment it out the the call to the draw routine and see what FPS you're running at.
In general, from a sampling stand point you may see it jitter in value if only measuring over 1 seconds.
Many drawing back ends only work in even divisors of 60. So if you were doing a lot of stuff it's possible one or two extra calls will bring you from 60/1 = 60 fps to 60/2 = 30 fps.

Related

Android: Understanding OnDrawFrame, FPS and VSync (OpenGL ES 2.0)

For a while now I've experienced an intermittent 'stuttering' of the sprites that are in motion within my Android Game. It's a fiarly simple 2D OpenGL ES 2.0 game. (This is an ongoing problem which I have re-visited many times).
In my game loop, I have 2 'timers' - one which will log the number of frames in the previous second, and another which counts the time (in Milliseconds) from the end of the current onDrawFrame iteration to the start of the next.
This is what I've found:
When not rendering anything, I get 60fps (for the most part), and every time onDrawFrame is called, it reports itself as taking longer that 16.667ms. Now, If I render something (doesn't matter if it's 1 quad or 100 quads, the result is the same), I get 60fps (for the most part) but now, only about 20% of onDrawFrame calls report themselves as taking longer than 16.667ms from the last call.
I don't really understand why this happens, firstly, why, when onDrawFrame isn't doing anything, is it called so 'slowly' - and more importantly, why does any GL call (one simple quad), still make the time between onDrawFrame calls longer than 16.667ms (albeit much less frequently).
I should say that when onDrawFrame reports taking longer than 16.667ms from the last iteration, it is almost always accompanied by a drop in FPS (to 58 or 59), but not all of the time, sometimes, the FPS stays constant. And conversely, sometimes when the FPS drops, onDrawFrame is called within 16.667ms of the last iteration completing.
So......
I'm trying to fix my game-loop and eradicate these 'stutters' - some other things to note:
When I do method profiling, it shows glSwapBuffers, sometimes taking a long time
When I do a GL Trace, most scenes its says renders in less than 1ms, but sometimes the odd frame takes 3.5-4ms - same scene. Nothing changes apart from the time it takes
Almost every time a frame is dropped, or onDrawFrame reports a long delay (or both), there is a visual glitch, but not every time. Big visual glitches seems to coincide with multiple 'delayed' onDrawFrame calls and /or dropped frames.
I don't think this is a scene complexity issue for 2 reasons: 1) even if I render my scene twice, it doesn't make the problem any worse, I still for the most part, get 60FPS with the occasional drop, just as before and 2), even if I strip the scene bare, I still get the problem.
I obviously am misunderstanding something, so a push in the right direction would be appreciated.
OnDrawFrame
#Override
public void onDrawFrame(GL10 gl) {
startTime = System.nanoTime();
fps++;
totalTime = System.nanoTime() - timeSinceLastCalled;
if (totalTime > 16667000) {
Log.v("Logging","Time between onDrawFrame calls: " + (totalTime /(double)1000000));
}
//Grab time
newTime = System.currentTimeMillis() * 0.001;
frameTime = newTime - currentTime; //Time the last frame took
if (frameTime > 0.25)
frameTime = 0.25;
currentTime = newTime;
accumulator += frameTime;
while (accumulator >= dt){
saveGameState();
updateLogic();
accumulator -= dt;
}
interpolation = (float) (accumulator / dt);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
render(interpolation);
if (startTime > lastSecond + 1000000000) {
lastSecond = startTime;
Log.v("Logging","fps: "+fps);
fps=0;
}
endTime = startTime;
timeSinceLastCalled = System.nanoTime();
}
This game loop above is the one featured in this excellent article.
Some thoughts:
Don't use System.currentTimeMillis() for timing things. It's based on the wall clock, which can be updated by the network. Use System.nanoTime(), which is based off the monotonic clock.
See this appendix for some notes on game loops. Queue-stuffing is fine for many things, but understand that you're not exactly working off of VSYNC, so timings will tend to be inaccurate.
Some devices (notably those based on qcom SOCs) reduce CPU speed when they think they're idle. Always take timings while actively moving your finger around on the touch screen.
If you want to debug frame rate issues you need to be using systrace. The traceview profiling isn't that useful here.
See Grafika's "record GL app" Activity for an example of a simple GLES app that drops frames, but adjusts the animation such that it's rarely noticeable.

Android OpenGL previous frame re-appear for split moment when calling update

I have just started using the OpenGL ES 2 for android for my little game and have encountered a problem on redrawing the screen on each frame.
I have setup a loop on my Renderer's onDrawFrame, just a simple [ updateGameLogic() -> drawGame() ] or Thread.sleep() loop based on the time lapsed from last drawGame call.
Currently the updateGameLogic() method simply translate the camera to the +ve X direction (the game is 2d).
In the drawGame() call, I first clear my screen with GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT). Then I have 3 glBindTexture and glDrawElements calls for drawing 3 categories of objects with different texture atlas.
Here comes the problem, in between each frame drawn on screen, there is a blink of the previous frame appearing which is undesired and makes the game look dizzy. Precisely, say the game is just about to draw frame 3 from frame 2, right before frame 2 vanish and frame 3 appearing, there is a split moment where frame 1 is displayed.
I thought this may be due to the way the GLSurfaceView is buffered by the system so I experimented with calling multiple glClear before drawing but everything stays the same. Would be grateful if someone can provide some explanation / solution to the problem, and what I have done wrong, thanks. (basically paragraph 2 to 4 is all my code so I have not posted it, unless requested)
From the clarification in the comment, it sounds like you have something like this in your code:
public void onDrawFrame(GL10 gl) {
long currentTime = SystemClock.elapsedRealtime();
long deltaTime = currentTime - mLastFrameTime;
if (deltaTime < 33) {
Thread.sleep(33 - deltaTime);
return;
}
mLastFrameTime = currentTime;
updateGameLogic(deltaTime);
drawGame();
}
This will indeed cause problems. When onDrawFrame() is called, you have to render a frame. You can't just return without drawing anything. The caller will assume that you rendered a frame in any case, and it will end up being presented on the screen. If you decide not to render anything, whatever happened to be in the surface you were supposed to draw to will be presented. There's no telling what this will be, but it's quite likely that it's an old frame from 2-3 frames earlier.
If you want to artificially throttle the frame rate, e.g. to save power, unfortunately there's no very good way to do this in Android. Using sleeps in onDrawFrame() is kind of dirty (and inherently unreliable, IMHO), but it might be necessary in this case. The key is that either before or after you sleep, you still need to render a frame. As a first attempt, I would try tweaking the above to something like this:
public void onDrawFrame(GL10 gl) {
long currentTime = SystemClock.elapsedRealtime();
long deltaTime = currentTime - mLastFrameTime;
if (deltaTime < 33) {
Thread.sleep(33 - deltaTime);
currentTime = SystemClock.elapsedRealtime();
deltaTime = currentTime - mLastFrameTime;
}
mLastFrameTime = currentTime;
updateGameLogic(deltaTime);
drawGame();
}
Note that while there is still an artificial delay, there is no early return in the code anymore.
There are probably more robust variations of this idea for throttling the redraws to 30 fps. Some searching on SO or the rest of the internet should reveal previous discussions.

Background image taking too long to draw (Canvas) Jerky Sprites......?

Hey all I'm at a crossroads with my app that I've been working on.
It's a game and an 'arcade / action' one at that, but I've coded it using Surfaceview rather than Open GL (it just turned out that way as the game changed drastically from it's original design).
I find myself plagued with performance issues and not even in the game, but just in the first activity which is an animated menu (full screen background with about 8 sprites floating across the screen).
Even with this small amount of sprites, I can't get perfectly smooth movement. They move smoothly for a while and then it goes 'choppy' or 'jerky' for a split second.
I noticed that (from what I can tell) the background (a pre-scaled image) is taking about 7 to 8 ms to draw. Is this reasonable? I've experimented with different ways of drawing such as:
canvas.drawBitmap(scaledBackground, 0, 0, null);
the above code produces roughly the same results as:
canvas.drawBitmap(scaledBackground, null, screen, null);
However, if I change my holder to:
getHolder().setFormat(PixelFormat.RGBA_8888);
The the drawing of the bitmap shoots up to about 13 MS (I am assuming because it then has to convert to RGB_8888 format.
The strange thing is that the rendering and logic move at a very steady 30fps, it doesn't drop any frames and there is no Garbage Collection happening during run-time.
I've tried pretty much everything I can think of to get my sprites moving smoothly
I recently incorporated interpolation into my gameloop:
float interpolation = (float)(System.nanoTime() + skipTicks - nextGameTick)
/ (float)(skipTicks);
I then pass this into my draw() method:
onDraw(interpolate)
I have had some success with this and it has really helped smooth things out, but I'm still not happy with the results.
Can any one give me any final tips on maybe reducing the time taken to draw my bitmaps or any other tips on what may be causing this or do you think it's simply a case of Surfaceview not being up to the task and therefore, should I scrap the app as it were and start again with Open GL?
This is my main game loop:
int TICKS_PER_SECOND = 30;
int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
int MAX_FRAMESKIP = 10;
long next_game_tick = GetTickCount();
int loops;
bool game_is_running = true;
while( game_is_running ) {
loops = 0;
while( GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP) {
update_game();
next_game_tick += SKIP_TICKS;
loops++;
}
interpolation = float( GetTickCount() + SKIP_TICKS - next_game_tick )
/ float( SKIP_TICKS );
display_game( interpolation );
}
Thanks
You shouldn't use Canvas to draw fast sprites, especially if you're drawing a fullscreen image. Takes way too long, I tell you from experience. I believe Canvas is not hardware accelerated, which is the main reason you'll never get good performance out of it. Even simple sprites start to move slow when there are ~15 on screen. Switch to OpenGL, make an orthographic projection and for every Sprite make a textured quad. Believe me, I did it, and it's worth the effort.
EDIT: Actually, instead of a SurfaceView, the OpenGL way is to use a GLSurfaceView. You create your own class, derive from it, implement surfaceCreated, surfaceDestroyed and surfaceChanged, then you derive from Renderer too and connect both. Renderer handles an onDraw() function, which is what will render, GLSurfaceView manages how you will render (bit depth, render modes, etc.)

Android: How to find the frame rate of a device?

Frame rate: I'm referring to the rate at which display changes. i.e. Ondraw() is called and the canvas is redrawn.
Is there a default rate for all android devices ? As this rate depends on the processing power of the device , How to find out the frame rate of a device , before starting to program for that mobile device ?
This may be a follow-up to this question, where I suggested that having a redraw loop that just kept drawing over and over again might be a bit excessive. There may be an api to find out the capabilities of the devices display, but if there is I'm not aware of it. When you're writing your own event loop / thread function you can control the framerate by how often you call your 'draw' method. Typically, I think for most purposes, you would be ok with a refresh rate of 30 or so. If you're writing a fast action game, that needs rapid animation then you may want to run as fast as you can, the more fps, the smoother it will be.
A typical event loop (thread run function) might look something like this:
// define the target fps
private static final int UPDATE_RATE = 30; // Frames per second (fps)
public void run() {
while(running) { // volatile flag, set somewhere else to shutdown
long beginTimeMillis, timeTakenMillis, timeLeftMillis;
// get the time before updates/draw
beginTimeMillis = System.currentTimeMillis();
// do the thread processing / draw
performUpdates(); // move things if required
draw(); // draw them on the screen
// get the time after processing and calculate the difference
timeTakenMillis = System.currentTimeMillis() - beginTimeMillis;
// check how long there is until we reach the desired refresh rate
timeLeftMillis = (1000L / UPDATE_RATE) - timeTakenMillis;
// set some kind of minimum to prevent spinning
if (timeLeftMillis < 5) {
timeLeftMillis = 5; // Set a minimum
}
// sleep until the end of the current frame
try {
TimeUnit.MILLISECONDS.sleep(timeLeftMillis);
} catch (InterruptedException ie) {
}
}
}
You can use the dumpsys tool provided by Android. To obtain information about the display of the device execute the command:
adb shell dumpsys display
The information about the frame rate of the device is provided in the attribute "mPhys".
You will find something like:
mPhys=PhysicalDisplayInfo{1080x1920, 60.000004 fps, densitiy 3.0,
480.0x480.0 dpi, secure true}
The frame rate of the device is in the second field, in my case is 60.000004 fps
You can't rely on a certain framerate. Android is a using multitasking operating system. If there are some threads running in the background that do some heavy lifting, you might not be able to reach the framerate you want. Even if you're the only active process, the framerate depends on your GPU and CPU, and the clock of each. Maybe the user has a hacked ROM that changes the clock to a custom value.
Some phones might be locked to a certain framerate. The HTC EVO was locked to 30fps for the longest time, until custom ROMs came out that removed that limitation. Newer EVO ROMs also removed that limitation.
I don't know what you're trying to do, but your best bet is to measure the time after each frame and use that delta for your animations. If you're trying to display the FPS, then use a smoothed average.
There is a simple tricky way to find device FPS during runtime.
Just call the following method:
long oneSecondLater=0;
int FPS=0;
int counter=0;
ValueAnimator v_animator;
private void logFPS()
{
oneSecondLater = System.currentTimeMillis()+1000;
v_animator = ValueAnimator.ofFloat(0.0f, 1.0f);
v_animator.setRepeatCount(ValueAnimator.INFINITE);
v_animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
FPS++;
if(System.currentTimeMillis() > oneSecondLater)
{
counter++;
if(counter>1)//ignore the first onAnimationUpdate call (it is not correct)
Log.i("checkFPS","FPS:" + FPS);
FPS=0;
oneSecondLater = System.currentTimeMillis()+1000;
}
}
});
v_animator.start();
}
I log FPS every second, The output of my Logcat was as follows
It works because for ValueAnimator, onAnimationUpdate() method will call according to device FPS;
This might be an old question, but for future reference, I found this library named Takt
https://github.com/wasabeef/Takt.
Takt is Android library for measuring the FPS using Choreographer.

Android loading and showing a lot of images in ImageView(frame by frame animation) hangs in certain moments

I've created an application that show around 250 images in ImageView. Images are loaded one after another, 15-30 images per second. Basically the whole thing gives an illusion of a rotating 3D object, at least it should.
The problem is next, app hangs when loading certain images(i.e. I see a few seconds of fluid animation and then animation hangs, jump 10-15 frames(images) ahead and continues. It always happens at the same places in animation cycle.
I though that Android might not have enough resources to handle something like this, so I've resized images to half their size, but it did't help. I've tried buffering images but that did't help either(actually, maybe a little, I think that animation looks a little bit smoother).
And now the weirdest thing. I use the touch screen to allow users to "rotate" the 3D object on those images, and while rotating I again experience those hangs at exactly the same places as with the animation.
All images are in .png format and their size vary from 15kB to 40kB.
I use the following code for the animation:
new Thread(new Runnable() {
#Override
public void run() {
while (!stopStartupAnimation && li < images_360.length) {
final int fli = li;
handler.post(new Runnable() {
#Override
public void run() {
//Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
//imageCanvas.setImageResource(images_360[fli]);
imageCanvas.setImageBitmap(imageStackNext.pop());
System.out.println("rawX = " + fli);
}
});
int ti = fli +25;
if(ti > images_360.length-1){
ti = ti - images_360.length;
}
imageStackNext.push(BitmapFactory.decodeResource(getResources(), images_360[ti]));
synchronized (this) {
try {
wait(1000 / 25);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
li++;
li++;
if (li >= images_360.length) {
li = 0;
}
}
}
}).start();
First, 15-40KB is their compressed form. Uncompressed, as Bitmaps, they are probably substantially larger. 250 of them may be using many MB of RAM, which is not a good idea.
Second, given a choice between using OpenGL for 3D (which is its purpose), or the 2D drawing primitives on the Canvas, or using ImageView, you chose the worst-performing option.
Third, postRunnable() does not take effect immediately, but rather puts things on a message queue for the main application thread to process when it gets a chance. If it gets tied up -- say, handling touch events -- it may well skip over some seemingly redundant ImageView redraws, or have them go by so fast they appear to not happen. All your 40ms wait() does is ensure that you are only raising events every 40ms, not that they will paint every 40ms. Besides, you could have more easily just used postDelayed() for your 40ms timing.
Bitmaps should be loaded efficiently.
Refer example on official page: https://developer.android.com/training/displaying-bitmaps/index.html

Categories

Resources