2011-08-28 2 views
0

나는 아직 다루지 않은 것들 중 하나이기 때문에 OpenGL 렌더링을 가르치기 위해 약간의 게임을 작성하고 있습니다. 이전에 SDL을 사용했는데 여전히 성능이 좋았지 만 지금과 똑같은 기능을 수행하지 못했습니다.이 기능을 어떻게 최적화 할 수 있습니까? (거의 모든 처리 능력을 사용합니다.)

기본적으로 내 게임에는 아직 많은 기본적인 움직임과 배경 그림이 없습니다. OpenGL로 전환했을 때, 마치 의 방식으로이 너무 빠릅니다. 초당 프레임 수는 2000을 초과하며이 기능은 대부분의 처리 능력을 사용합니다.

재미있는 점은 100 % CPU를 사용했지만 원활하게 실행되는 SDL 버전의 프로그램은 OpenGL 버전이 약 40-60 % CPU만을 사용하지만 내 그래픽 카드에 과세하는 것으로 보입니다. 바탕 화면이 응답하지 않게됩니다. 나쁜.

너무 복잡하지는 않지만 플레이어 그래픽 자체가 가운데에 고정되어있는 동안 플레이어의 X 및 Y 좌표에 따라 1024x1024 배경 타일을 렌더링하여 움직임이있는 느낌을줍니다. 더 큰 화면을위한 작은 타일이기 때문에 여러 번 타일을 엮어서 전체 배경을 만들어야합니다. 아래의 코드에서 두 개의 for 루프는 12 번 반복되어 반복되므로 초당 2000 회 호출 할 때 이것이 효과적이지 않은 이유를 알 수 있습니다.

그래서 지점에 도착, 이것은 악을 행하는 것입니다 : 내가 인위적으로 게임 루프에서 호출 SDL_Delay(1)하여 속도를 제한하는 경우

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

, 나는 660 ~에 ± 20 아래로 FPS를 잘라, 나는 "과다한 성과"를 얻지 못한다. 그러나 이것이 그것이 올바른 방법인지 의심 스럽습니다. 완료를 위해서

이 내 일반 렌더링 및 게임 루프 기능은 다음과 같습니다

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 %를 지출 내 배경 렌더링.

+5

프레임 속도를 제한하는 타이머를 만드십시오 ... – Jason

+0

뭔가를 놓치지 않는 한이 전체 기능을 [glCallList]로 작성할 수 없습니다 (http://www.opengl.org/sdk/docs/). man/xhtml/glCallList.xml) 그런 다음 [glTranslatef] (http://www.opengl.org/sdk/docs/man/xhtml/glTransl.xml)에 대한 호출을 사용하여 그려지는 위치를 제어 할 수 있습니까? – Flexo

+1

유휴 처리는 나쁜 것입니다. 더 나은 성능을 가지면 실제로 새로운 문제가 발생합니다. 유휴 처리 대신 멀티미디어 타이머를 사용하십시오. –

답변

6

VSYNC를 켭니다. 이렇게하면 그래픽 데이터를 디스플레이가 사용자에게 제공 할 수있는 속도만큼 정확히 계산할 수 있으며, CPU 또는 GPU주기를 낭비하지 않고 모니터가 여전히 이전 프레임을 표시 하느라 사용하지 않아서 버려지는 여분의 프레임을 계산합니다 .

+0

내가 기억하는 한, VSYNC는 전체 화면에서만 작동하는데 여기서는 사용하지 않습니다. 내 기억이 내게 거짓말입니까? – LukeN

+0

@LukeN : 그 종류의 제한은 플랫폼에 따라 다를 수 있습니다. 어쨌든, 나는 그것을 시도하는 것이 좋습니다. –

+0

@LukeN : VSync는 전체 화면과 관련이 없거나 독립적입니다. – datenwolf

3

우선 타일을 x * y 번 렌더링 할 필요가 없습니다. 커버해야하는 전체 영역에 대해 한 번 렌더링하고 GL_REPEAT를 사용하여 OpenGL에서 전체 영역을 커버하도록 할 수 있습니다. 타일이 왜곡되지 않도록 (스트레칭) 적절한 텍스처 좌표를 한 번 계산하면됩니다. 움직이는 것처럼 보이게하려면 프레임마다 작은 여백만큼 텍스처 좌표를 늘리십시오.

이제 속도를 제한합니다. 당신이 원하는 것은 그냥 하나 개의 완전한 프레임을 렌더링하는 데 걸리는 시간이에 수면() 호출을 연결하되 측정 할되지 않습니다 :

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와의 결합과 달리 임의의 프레임 속도를 허용하는 이점이 있습니다.

+0

임의의 프레임 속도는 쓸모가 없습니다. 의미있는 유일한 비율 제한은 사용자의 디스플레이와 일치하는 것입니다. 사용자의 디스플레이 시스템이 60Hz, 72Hz, 75Hz, 85Hz (또는 고급 하드웨어에서 100Hz 또는 120Hz)로 실행되는지 여부를 어떻게 결정합니까? –

+0

당신은 사수 게임을 본 적이 없습니까? 귀하의 의견은 말도 안돼. 디스플레이 업데이트 (예 : 물리 또는 인공 지능 업데이트)보다 더 많은 것들이 프레임 속도에 묶일 수 있습니다. – karx11erx

+0

모션, 히트 테스트, 마우스 입력 읽기 등을 렌더링 속도보다 자주 시뮬레이션하는 것이 효과적 일 수 있습니다. 그러나 디스플레이 시스템이 받아 들일 수있는 것보다 더 자주 렌더링하는 것이 결코 이점이 아닙니다. (이것은 실시간 재생을위한 것으로 가정하고, 나중에 재생할 비디오 파일을 생성하는 경우 가능한 한 빨리 렌더링하지만 디스플레이에 해당하는 재생 속도로 렌더링합니다.) –

1

2000fps에서는 전체 프레임을 렌더링하는 데 단지 0.5ms 밖에 걸리지 않습니다. 60 FPS를 원한다면 각 프레임은 약 16 밀리 초가 걸릴 것입니다.이렇게하려면 먼저 프레임 (약 0.5ms)을 렌더링 한 다음 SDL_Delay()을 사용하여 나머지 16ms를 사용하십시오.

또한 코드를 프로파일 링하는 데 관심이있는 경우 (2000FPS를 얻는 경우 필요하지 않음) High Resolution Timers을 사용할 수 있습니다. 그렇게하면 프로그램이 코드에서 얼마나 많은 시간을 소비하는지뿐만 아니라 코드 블록이 얼마나 오래 걸릴지를 정확하게 알 수 있습니다.

관련 문제