How To Limit Framerate When Using Android's Glsurfaceview.rendermode_continuously?
Solution 1:
The solution from Mark is almost good, but not entirely correct. The problem is that the swap itself takes a considerable amount of time (especially if the video driver is caching instructions). Therefore you have to take that into account or you'll end with a lower frame rate than desired. So the thing should be:
somewhere at the start (like the constructor):
startTime = System.currentTimeMillis();
then in the render loop:
publicvoidonDrawFrame(GL10 gl)
{
endTime = System.currentTimeMillis();
dt = endTime - startTime;
if (dt < 33)
Thread.Sleep(33 - dt);
startTime = System.currentTimeMillis();
UpdateGame(dt);
RenderGame(gl);
}
This way you will take into account the time it takes to swap the buffers and the time to draw the frame.
Solution 2:
When using GLSurfaceView, you perform the drawing in your Renderer's onDrawFrame which is handled in a separate thread by the GLSurfaceView. Simply make sure that each call to onDrawFrame takes (1000/[frames]) milliseconds, in your case something like 33ms.
To do this: (in your onDrawFrame)
- Measure the current time before your start drawing using System.currentTimeMillis (Let's call it startTime)
- Perform the drawing
- Measure time again (Let's call it endTime)
- deltaT = endTime - starTime
- if deltaT < 33, sleep (33-deltaT)
That's it.
Solution 3:
Fili's answer looked great to me, bad sadly limited the FPS on my Android device to 25 FPS, even though I requested 30. I figured out that Thread.sleep()
works not accurately enough and sleeps longer than it should.
I found this implementation from the LWJGL project to do the job: https://github.com/LWJGL/lwjgl/blob/master/src/java/org/lwjgl/opengl/Sync.java
Solution 4:
Fili's solution is failing for some people, so I suspect it's sleeping until immediately after the next vsync instead of immediately before. I also feel that moving the sleep to the end of the function would give better results, because there it can pad out the current frame before the next vsync, instead of trying to compensate for the previous one. Thread.sleep() is inaccurate, but fortunately we only need it to be accurate to the nearest vsync period of 1/60s. The LWJGL code tyrondis posted a link to seems over-complicated for this situation, it's probably designed for when vsync is disabled or unavailable, which should not be the case in the context of this question.
I would try something like this:
privatelong lastTick = System.currentTimeMillis();
publicvoidonDrawFrame(GL10 gl){
UpdateGame(dt);
RenderGame(gl);
// Subtract 10 from the desired period of 33ms to make generous// allowance for overhead and inaccuracy; vsync will take up the slacklong nextTick = lastTick + 23;
long now;
while ((now = System.currentTimeMillis()) < nextTick)
Thread.sleep(nextTick - now);
lastTick = now;
}
Solution 5:
If you don't want to rely on Thread.sleep
, use the following
double frameStartTime = (double) System.nanoTime()/1000000;
// start time in milliseconds// using System.currentTimeMillis() is a bad idea// call this when you first start to drawint frameRate = 30;
double frameInterval = (double) 1000/frame_rate;
// 1s is 1000ms, ms is millisecond// 30 frame per seconds means one frame is 1s/30 = 1000ms/30publicvoidonDrawFrame(GL10 gl){
double endTime = (double) System.nanoTime()/1000000;
double elapsedTime = endTime - frameStartTime;
if (elapsed >= frameInterval)
{
// call GLES20.glClear(...) hereUpdateGame(elapsedTime);
RenderGame(gl);
frameStartTime += frameInterval;
}
}
Post a Comment for "How To Limit Framerate When Using Android's Glsurfaceview.rendermode_continuously?"