WedX - журнал о программировании и компьютерных науках

Как можно оптимизировать эту функцию? (Использует почти всю вычислительную мощность)

Я сейчас пишу небольшую игру, чтобы научиться рендерингу OpenGL, поскольку это одна из тех вещей, которыми я еще не занимался. Раньше я использовал SDL, и эта же функция, хотя и работала плохо, не была такой чрезмерной, как сейчас.

По сути, в моей игре пока мало что происходит, только некоторые базовые движения и рисование фона. Когда я переключился на OpenGL, мне показалось, что это слишком слишком быстро. Мои кадры в секунду превышают 2000, и эта функция использует большую часть вычислительной мощности.

Интересно то, что программа в своей версии SDL использовала 100% ЦП, но работала без сбоев, в то время как версия OpenGL использует только около 40% - 60% ЦП, но, похоже, нагружает мою видеокарту таким образом, что весь мой рабочий стол перестает отвечать на запросы. Плохой.

Это не слишком сложная функция, она визуализирует фоновый тайл 1024x1024 в соответствии с координатами X и Y игрока, чтобы создать впечатление движения, в то время как само изображение игрока остается заблокированным в центре. Поскольку это маленькая плитка для большого экрана, мне приходится визуализировать ее несколько раз, чтобы сшить плитки вместе для получения полного фона. Два цикла for в приведенном ниже коде повторяются 12 раз вместе, поэтому я могу понять, почему это неэффективно при вызове 2000 раз в секунду.

Итак, чтобы перейти к делу, это злодей:

void render_background(game_t *game)
{
    int bgw;
    int bgh;

    int x, y;

    glBindTexture(GL_TEXTURE_2D, game->art_background);
    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &bgw);
    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &bgh);

    glBegin(GL_QUADS);

    /*
     * Start one background tile too early and end one too late
     * so the player can not outrun the background
     */
    for (x = -bgw; x < root->w + bgw; x += bgw)
    {
        for (y = -bgh; y < root->h + bgh; y += bgh)
        {
            /* Offsets */
            int ox = x + (int)game->player->x % bgw;
            int oy = y + (int)game->player->y % bgh;

            /* Top Left */
            glTexCoord2f(0, 0);
            glVertex3f(ox, oy, 0);

            /* Top Right */
            glTexCoord2f(1, 0);
            glVertex3f(ox + bgw, oy, 0);

            /* Bottom Right */
            glTexCoord2f(1, 1);
            glVertex3f(ox + bgw, oy + bgh, 0);

            /* Bottom Left */
            glTexCoord2f(0, 1);
            glVertex3f(ox, oy + bgh, 0);
        }
    }

    glEnd();
}

Если я искусственно ограничиваю скорость, вызывая SDL_Delay(1) в игровом цикле, я уменьшаю FPS до ~ 660 ± 20, я не получаю «излишка производительности». Но я сомневаюсь, что это правильный способ говорить об этом.

В заключение, вот мои общие функции рендеринга и игрового цикла:

void game_main()
{
    long current_ticks = 0;
    long elapsed_ticks;
    long last_ticks = SDL_GetTicks();

    game_t game;
    object_t player;

    if (init_game(&game) != 0)
        return;

    init_player(&player);
    game.player = &player;

    /* game_init() */
    while (!game.quit)
    {
        /* Update number of ticks since last loop */
        current_ticks = SDL_GetTicks();
        elapsed_ticks = current_ticks - last_ticks;

        last_ticks = current_ticks;

        game_handle_inputs(elapsed_ticks, &game);
        game_update(elapsed_ticks, &game);

        game_render(elapsed_ticks, &game);

        /* Lagging stops if I enable this */
        /* SDL_Delay(1); */
    }

    cleanup_game(&game);


    return;
}

void game_render(long elapsed_ticks, game_t *game)
{
    game->tick_counter += elapsed_ticks;

    if (game->tick_counter >= 1000)
    {
        game->fps = game->frame_counter;
        game->tick_counter = 0;
        game->frame_counter = 0;

        printf("FPS: %d\n", game->fps);
    }

    render_background(game);
    render_objects(game);

    SDL_GL_SwapBuffers();
    game->frame_counter++;

    return;
}

Согласно профилированию gprof, даже когда я ограничиваю выполнение с помощью SDL_Delay(), он все равно тратит около 50% времени на рендеринг моего фона.

28.08.2011

  • Создайте таймер для ограничения частоты кадров ... 28.08.2011
  • Если я чего-то не упустил, не могли бы вы написать всю эту функцию как glCallList, а затем используйте при вызове glTranslatef контролировать, где оно будет нарисовано? 28.08.2011
  • Простая обработка - это плохо: имея лучшую производительность, вы фактически создаете новые проблемы. Используйте мультимедийный таймер вместо обработки в режиме ожидания. 28.08.2011
  • gprof сообщит вам только собственное время кода, привязанного к процессору, плюс он пытается вывести инклюзивное время кода более высокого уровня очень сомнительным методом. Не ждите, что он вообще вам многое скажет. Если вы используете Linux действительно, попробуйте Zoom. 29.08.2011

Ответы:


1

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

28.08.2011
  • Насколько я помню, VSYNC работает только в полноэкранном режиме, который я здесь не использую .. мне врет моя память? 28.08.2011
  • @LukeN: Такое ограничение может зависеть от платформы. В любом случае, я рекомендую вам просто попробовать. 28.08.2011
  • @LukeN: VSync не зависит от полноэкранного режима или нет. 28.08.2011
  • Прежде всего, ограничение кадра V-Sync, установленное через драйвер, является общесистемным, что может вам не понадобиться. Кроме того, это не всегда доступно в других ОС. Если вы хотите сделать это программно, вы также в значительной степени ограничены Windows. Так что это не очень хорошая идея. 29.08.2011
  • @karx: Я не рекомендую решение на уровне драйверов. API-интерфейсы wgl и glx предоставляют функции для управления vsync для каждого контекста. 29.08.2011
  • Я никогда не читал об этом. У вас есть ссылка? У меня есть один: stackoverflow.com / questions / 589064 /. Согласно тому, что там было сказано, вы ошибаетесь. 29.08.2011
  • Я такого раньше не видел - а мне это могло понадобиться. Теперь мне интересно, насколько вы можете рассчитывать на наличие этого расширения на Linux-компьютерах. Внутриигровое решение будет работать всегда. 30.08.2011

  • 2

    Прежде всего, вам не нужно визуализировать плитку x * y раз - вы можете визуализировать ее один раз для всей области, которую она должна покрыть, и использовать GL_REPEAT, чтобы OpenGL покрыл ею всю область. Все, что вам нужно сделать, это один раз вычислить правильные координаты текстуры, чтобы плитка не искажалась (не растягивалась). Чтобы он выглядел движущимся, увеличивайте координаты текстуры с небольшим полем каждый кадр.

    Теперь перейдем к ограничению скорости. Что вы хотите сделать, так это не просто подключить туда вызов sleep (), а измерить время, необходимое для визуализации одного полного кадра:

    function FrameCap (time_t desiredFrameTime, time_t actualFrameTime)
    {
       time_t delay = 1000 / desiredFrameTime;
       if (desiredFrameTime > actualFrameTime)
          sleep (desiredFrameTime - actualFrameTime); // there is a small imprecision here
    }
    
    time_t startTime = (time_t) SDL_GetTicks ();
    // render frame
    FrameCap ((time_t) SDL_GetTicks () - startTime);
    

    Есть способы сделать это более точным (например, используя функции счетчика производительности в Windows 7 или используя разрешение в микросекундах в Linux), но я думаю, вы уловили общую идею. Этот подход также имеет то преимущество, что он не зависит от драйвера и, в отличие от связи с V-Sync, допускает произвольную частоту кадров.

    29.08.2011
  • Произвольная частота кадров бесполезна. Единственное, что имеет смысл, - это тот, который соответствует отображению пользователя. Как вы предлагаете определить, работает ли система отображения пользователя на частоте 60 Гц, 72 Гц, 75 Гц, 85 Гц (или даже 100 Гц или 120 Гц на высокопроизводительном оборудовании)? 29.08.2011
  • Вы никогда не играли в шутеры, не так ли? Ваш комментарий - ерунда. С частотой кадров можно связать гораздо больше, чем просто обновление дисплея (например, обновления физики или AI). 29.08.2011
  • Может быть полезно имитировать движение, проверять попадание, считывать данные, введенные мышью, и т. Д. Чаще, чем скорость рендеринга. Но никогда не бывает преимуществ от более частого рендеринга, чем может принять система отображения. (Предполагая, что это для воспроизведения в реальном времени, если вы создаете видеофайл для последующего воспроизведения, то выполняйте рендеринг как можно быстрее. Но вы все равно выполняете рендеринг для скорости воспроизведения, соответствующей дисплею.) 30.08.2011
  • Если вы не поместите этот материал в отдельные потоки, вам придется настроить его с помощью некоторого внутриигрового кадра. 30.08.2011

  • 3

    При 2000 FPS для рендеринга всего кадра требуется всего 0,5 мс. Если вы хотите получить 60 FPS, то каждый кадр должен занимать около 16 мс. Для этого сначала визуализируйте свой кадр (около 0,5 мс), затем используйте SDL_Delay(), чтобы использовать оставшиеся 16 мс.

    Кроме того, если вы заинтересованы в профилировании своего кода (что не требуется, если вы получаете 2000 FPS!), Вы можете использовать Таймеры высокого разрешения. Таким образом, вы могли точно сказать, сколько времени занимает любой блок кода, а не только сколько времени ваша программа тратит на него.

    03.09.2011
    Новые материалы

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..

    ИИ в аэрокосмической отрасли
    Каждый полет – это шаг вперед к великой мечте. Чтобы это происходило в их собственном темпе, необходима команда астронавтов для погони за космосом и команда технического обслуживания..


    Для любых предложений по сайту: [email protected]