Не может отследить кадры, отрисованные из другого потока

Наша компания разрабатывает несколько игр для мобильных платформ, включая Android. Мы используем OpenGL для всех визуальных элементов, включая пользовательский интерфейс (более подробные технические сведения приведены ниже).

Мы получили несколько странных предупреждений от консоли Google Play в отчете перед запуском, например: "Вашему приложению потребовалось 20764 мсек для запуска". На видео, представленном в этом отчете, запуск игры занял около секунды.

После некоторого расследования мы обнаружили, что Android Systrace не может обнаружить ничьи OpenGL, сделанные из другого потока. Поэтому тесты перед запуском (ошибочно) считают, что наша игра очень медленная.

Есть ли какой-нибудь способ уведомить систему о том, что рамка нарисована? Кажется, что eglSwapBuffers() недостаточно.

Есть ссылка на ту же проблему с Cocos2d: https://discuss.cocos2d-x.org/t/frozen-frames-warnings-by-google-play-pre-launch-report-for-3-17-cocos-demo-app/42894

Некоторые детали

Когда новая сборка публикуется в консоли Google Play, некоторые автоматические тесты выполняются на разных устройствах. Результаты этих тестов доступны в разделе "Отчет перед запуском" консоли Google Play.

С начала апреля мы получаем странные предупреждения о производительности на некоторых устройствах (всегда одни и те же). Два примера:

  • Время запуска: запуск вашего приложения занял 20764 мс...
  • Замороженные кадры: для рендеринга 33,33% кадров потребовалось больше 700 мс

Обе проблемы звучат ужасно - если бы они были правдой. Но когда мы изучали видеоролики тестирования, мы не увидели никаких проблем. Все игры начались довольно быстро и проходили без визуального заикания.

Отчет Systrace

Это изображение systrace, показывающее 5 секунд запуска нашей игры (я нарисовал прямоугольники).

Systrace

Как вы можете видеть, systrace нашел только 4 отрисованных кадра (розовый прямоугольник), которые были взяты из RenderThread. Но по какой-то причине Android не может обнаружить наши вызовы отрисовки GL, которые выполняются в другом потоке (синие линии).

Отчеты перед запуском также отображают только 3–4 кадра, каждый длиной 300–400 мс.

Код инициализации

Наш игровой движок запускает всю игровую логику и выводит код в отдельном потоке. Это упрощенный код инициализации.

Рабочий поток создается из переопределенного метода onStart() нашей Деятельности.

public class MyActivity extends Activity
{
    protected Thread worker = null;

    private native void Run();

    @Override
    protected void onStart()
    {
        super.onStart();

        if(worker == null)
        {
            worker = new Thread()
            {
                public void run()
                {
                    Run();
                }
            };

            worker.start();
        }
    }
}

Единственное, что делает поток - это встроенная функция Run(). Эта функция может быть преобразована во что-то вроде этого:

void MyActivity::Run()
{
    initApp();

    while(!destroyRequested())
    {
        // Process the game logic.
        if (activated && window != NULL)
        {
            time->process();
            input->process();
            sound->process();
            logic->process();
            graphics->draw();
        }
    }

    clearApp();
}

Как видите, рабочий поток постоянно вращает цикл обновления и отрисовки. Vsync защищает цикл от чрезмерной производительности. Тяжелые операции, такие как загрузка ресурсов, выполняются асинхронно, чтобы избежать зависаний.

Со стороны пользователя этот подход работает просто отлично. Игры загружаются быстро и идут гладко.

0 ответов

Другие вопросы по тегам