Странное использование процессора в Java с использованием LibGdx

1

В настоящее время я разрабатываю небольшую настольную java-игру, используя инфраструктуру libgdx. Все отлично работает, за исключением некоторых действительно странных особенностей использования ЦП.

При запуске только фреймворка с пустым циклом рендеринга я получаю около 28% использования ЦП. Это кажется хорошим, учитывая, что lbgdx также выполняет некоторое управление, а метод render вызывается как можно быстрее.

Но здесь все начинает сумасшедшим...

При рисовании и обновлении около 200 объектов загрузка процессора увеличивается примерно до 55%. Однако Libgdx говорит мне, что использует только один открытый вызов draw gl draw. Накладные расходы процессора должны быть действительно низкими, не так ли? Также использование ЦП не вызвано циклом обновления. Я уже попытался удалить его, но его использование по-прежнему составляет около 50%.

Но это становится еще хуже...

Я случайно узнал, что, когда программа должна обрабатывать множество объектов (около 50000), использование падает примерно до 30%.

Если я освобожу массивное количество объектов и вернусь к моим 200 объектам, загрузка процессора еще больше снизится до ~ 20%.

Если я повторю этот процесс, использование в конечном итоге снизится до 3%. Да 3%. И он останется на уровне 3%. (Пока я не выделяю никаких других объектов, использование, конечно, будет увеличиваться)

Подводя итог: я показываю ту же самую сцену. Сначала с 55% использования ЦП. И после выделения и освобождения огромного количества объектов с 3% использования ЦП.

Как это могло случиться?

Сначала я понял, что lidgdx использует пакетную систему, которая будет создавать партии вершин, информацию о цвете и координаты текстуры, чтобы минимизировать количество вызовов рисования. Если я задаю размер партии до 2500 спрайтов за партию, загрузка процессора снизится до 30% - 40%. Тем не менее, странный эффект выделения и снятия с охраны все еще происходит.

Я также дам вам мое обновление, рисование, рендеринг и очистить методы, если это поможет.

Метод рендеринга:

@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;
}

Метод обновления:

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);
    }
}

Метод рисования:

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();
}

Открытый метод:

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();
}

Я разрабатываю программу в eclipse 4.4.1 с помощью java 1.8.0_31-b13. Операционная система - Windows 8.1.

  • 0
    Как вы измеряете загрузку процессора?
  • 1
    Соотношение загрузки процессора с вашей частотой кадров?
Показать ещё 5 комментариев
Теги:
opengl
libgdx
cpu-usage

3 ответа

2

ваш метод render() немного странный. Я не знаю, влияет ли это на сумасшедшие тайм-ауты процессора, следующий код не нужен, потому что сам LibGDX обрабатывает время ожидания 16,6 мс (для 60 Гц). Это может вызвать некоторые проблемы, если ваш таймер не соответствует таймеру LibGDX. Поэтому вы можете подождать слишком долго и, следовательно, значительно снизить нагрузку процессора. При удалении объектов таймеры будут оставаться вне синхронизации → уменьшено использование процессора.

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

    try
    {
        Thread.sleep(0, 1000);
    }
    catch (InterruptedException e)
    {
        e.printStackTrace();
    }
}
  • 0
    Сначала: спасибо за ваш ответ! :) Вы правы насчет времени, которое LibGdx делает в фоновом режиме. Таким образом, мой временной код является избыточным. Однако, когда я удаляю блок кода, падение использования все еще там. Так что, похоже, это не главная проблема. Но я могу представить, что встроенный таймер libgdx каким-то образом запутывается из-за массового создания объекта.
0

Как я выяснил, это проблема, связанная с vSync и некоторыми конкретными драйверами (nVidia?). Вы можете использовать следующий код для предотвращения высокой загрузки ЦП:

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

Любой FPS, кроме 0 и 60, также работает:

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

Хорошо, поэтому после большого чтения и некоторых экспериментов я понял это!

В классе DesktopLauncher, который используется libGDX, программисту предлагается создать объект конфигурации для процесса инициализации lwjgl. Что-то вроде этого:

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);
}

Оказывается, последняя строка config.foregroundFPS = 0; имеет решающее значение. Это отключает все попытки фреймворка установить поток, чтобы спать, когда это возможно.

Я сильно подозреваю, что эта функциональность сна может быть применима для странного использования ЦП. После отключения этого режима поведение теперь происходит нормально.

Тем не менее, большое спасибо за вашу поддержку! :)

  • 0
    Можете ли вы принять свой ответ, чтобы было более очевидно, что вы нашли решение? Спасибо

Ещё вопросы

Сообщество Overcoder
Наверх
Меню