Excliff
Excliff

Reputation: 21

Strange CPU usage in Java using LibGdx

I'm currently developing a small desktop java game using the libgdx framework. Everything is woking fine, except some really strange CPU usage behaviour.

When running just the framework background with an empty rendering loop i get about 28% CPU usage. This seems fine considering lbgdx is also doing some management and the render method is called as fast as possible.

But here things start to get crazy ...

When drawing and updating about 200 objects, the CPU usage jumps up to about 55%. Libgdx however tells me it uses only a single open gl draw call. CPU overhead should be really low then, shouldn't it? Also the CPU usage is not caused by the update loop. I already tried removing it, but the usage is still up to about 50%.

But it gets even worse ...

I dicovered by accident that when the program has to handle a lot of objects (around 50000) the usage drops to about 30%.

If i deallocate the massive amount of objects then and go back to my 200 objects the CPU usage drops even further to ~20%.

If i repeate this process the usage will eventually drop to 3%. Yes 3%. And it'll stay at 3%. (As long as I'm not allocating any other objects. The usage will increase then, of course)

To sum it up: I'm rendering the exact same scene. At first with 55% CPU usage. And after allocating and deallocating a massive amount of objects with 3% CPU usage.

How could this possibly happen?

At first I figured that lidgdx is using a batch system, that will create batches of vertices, color information and texture coordinates to minimize the amount of draw calls. If I'll set the batch size to 2500 sprites per batch, the CPU usage will drop to 30% - 40%. However, the strange allocating and deallocating effect is still happening.

I'll also give you my update, draw, render and clear methods, if it helps.

Render method:

@Override
public void render()
{   
    //the basic game loop components
    //update all and draw everything
    update();
    draw();

    //Entity.alive refers to an ArrayList containing
    //all objects that'll be updated and draw in the
    //render loop
    System.out.println("ENTITY COUNT " + Entity.alive.size());

    //and remove all the dead bodies
    //from the carpet ... you know
    //i can't stand blood ...
    //and make some new ones, too 
    clear();

    while((System.nanoTime() / 1000000) - time_last < 16)
    {
        //nothing to do here ... (imagine meme)

        try
        {
            Thread.sleep(0, 1000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }

    time_last = System.nanoTime() / 1000000;
}

Update method:

public void update()
{   
    //update all the entities
    //in the "alive" ArrayList
    for(Entity e: Entity.alive) e.update();

    //add all entities declared
    //dead to the dead list
    for(Entity e: Entity.alive)
    {
        //not dead - nothing to do here
        if(e.getState() == true) continue;

        //else add to dead list!
        Entity.dead.add(e);
    }
}

Draw method:

public void draw()
{   
    //clear the screen
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);

    //update the view
    Draw.updateCamera();

    //start a batch section
    //all further calls will
    //be packed an sent to
    //gpu in one flush
    Draw.startBatch();

    //draw all living entities
    for(Entity e: Entity.alive) e.draw();

    //draw all the level
    //geometry and sprites
    Level.draw();       

    //ending the sprite batch
    Draw.endBatch();
}

Clear method:

public void clear()
{
    //remove all dead objects
    for(Entity e: Entity.dead)
    {           
        Entity.alive.remove(e);
    }

    //clear the array of the dead
    Entity.dead.clear();

    //add all the newly created
    //objects to the alive list
    for(Entity e: Entity.born)
    {
        Entity.alive.add(e);
    }

    //clear the creation list
    Entity.born.clear();
}

Im developing the program in eclipse 4.4.1 using java 1.8.0_31-b13. The operating system is Windows 8.1.

Upvotes: 1

Views: 1352

Answers (3)

Pavel
Pavel

Reputation: 2776

As I found, out this issue related to vSync and some specific (nVidia?) drivers. You may use following code to prevent high CPU usage:

LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.vSyncEnabled = false;

Any FPS except 0 and 60 works as well:

LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.backgroundFPS = 59;
config.foregroundFPS = 59;

Upvotes: 0

Excliff
Excliff

Reputation: 21

Ok, so after a lot of reading and some experiments i figured it out!

In the DesktopLauncher class that libGDX is using, the programmer is asked to create a config object for the lwjgl init process. Something like this:

public static void main(String[] args)
{
    LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();

    config.width = 1920;
    config.height = 1080;
    config.fullscreen = true;
    config.foregroundFPS = 0;

    new LwjglApplication(new Controller(), config);
}

It turns out the last line config.foregroundFPS = 0; is crucial. This disables all attempts of the framework to set the thread to sleep whenever possible.

I strongly suspect this sleep functionality to be resposible for the strange CPU usage. After disabling this the usage behaves normally now.

Still, thanks a lot for your support! :)

Upvotes: 0

laubed
laubed

Reputation: 248

your render() method is a bit weird. I don't know whether this contributes to the crazy cpu timings the following code is unnecessary because LibGDX itself handles the 16.6ms wait time (for 60Hz) itself. It might make some troubles if your timer doesn't match LibGDX timer. Therefore you might wait too long and thus reducing cpu load significantly. When removing the Entities the timers will stay out of sync -> reduced cpu usage.

while((System.nanoTime() / 1000000) - time_last < 16)
{
    //nothing to do here ... (imagine meme)

    try
    {
        Thread.sleep(0, 1000);
    }
    catch (InterruptedException e)
    {
        e.printStackTrace();
    }
}

Upvotes: 2

Related Questions