如何优化此功能? (几乎使用所有处理能力)

时间:2011-08-28 15:08:22

标签: c optimization opengl sdl

我正在编写一个小游戏来教我自己的OpenGL渲染,因为它是我尚未解决的问题之一。我之前使用过SDL,同样的功能,虽然仍然表现不佳,但并不像现在那样过顶。

基本上,我的游戏中没有太多进展,只是一些基本的动作和背景绘图。当我切换到OpenGL时,它看起来好像是方式太快了。我的每秒帧数超过2000,此功能耗尽了大部分处理能力。

有趣的是,它的SDL版本中的程序使用了100%的CPU但运行顺畅,而OpenGL版本只使用了大约40% - 60%的CPU,但似乎以我整个桌面的方式对我的显卡征税变得反应迟钝。坏。

这不是一个太复杂的功能,它根据玩家的X和Y坐标呈现1024x1024背景图块,以给出移动的印象,同时玩家图形本身保持锁定在中心。因为它是用于更大屏幕的小瓷砖,所以我必须多次渲染它以将瓷砖拼接在一起以获得完整的背景。下面代码中的两个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%的时间渲染我的背景。

3 个答案:

答案 0 :(得分:6)

开启VSYNC。这样你就可以像显示器一样快地计算图形数据给用户,并且你不会浪费CPU或GPU周期来计算中间的额外帧,因为显示器仍在忙着显示前一帧

答案 1 :(得分:3)

首先,您不需要渲染平铺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不同 - 允许任意帧速率。

答案 2 :(得分:1)

在2000 FPS时,渲染整个帧只需0.5 ms。如果你想获得60 FPS,那么每帧应该花费大约16 ms。要做到这一点,首先渲染你的帧(大约0.5毫秒),然后使用SDL_Delay()来消耗剩余的16毫秒。

此外,如果您有兴趣分析您的代码(如果您获得2000 FPS则不需要!),那么您可能想要使用High Resolution Timers。通过这种方式,您可以准确判断任何代码块需要多长时间,而不仅仅是程序花费的时间。