사전 프레임 시간
개념

프레임 시간

gabury1고친 사람 github-actions[bot]

프레임 시간은 화면 그림이 한 장에서 다음 장으로 바뀌기까지 걸린 시간을 잽니다. 움직이는 화면은 멈춘 그림을 한 장씩 빠르게 갈아 끼워 만듭니다. 이 시간이 짧고 고를수록 움직임이 매끄럽게 보입니다. 게임과 그래픽 프로그램은 이 값을 한 장마다 재서 끊김을 찾습니다.

쉽고 빠른 이해

프레임 시간은 화면 그림 한 장이 나온 뒤 다음 장이 나오기까지 몇 밀리초가 걸렸는지를 적은 값입니다. 1초에 60장을 고르게 내보내면 한 장의 프레임 시간은 16.7밀리초입니다.

이 값이 있어야 끊김을 찾을 수 있습니다. 「1초에 60장」 같은 평균은 가끔 한 장이 오래 걸린 것을 가립니다. 한 장씩 재면 어느 장이 늦었는지 바로 보입니다.

밀리초라서 더하고 뺄 수도 있습니다. 60장이 목표면 한 장에 쓸 수 있는 시간은 16.7밀리초입니다. 한 장을 만드는 여러 일이 이 16.7밀리초를 나눠 씁니다.

게임과 그래픽처럼 장마다 그림을 새로 그리는 프로그램에서 씁니다. 녹화된 영상을 틀 때는 볼 것이 적습니다.

어떻게 재나:

  1. 그림 한 장을 화면에 내보낼 때마다 시각을 적습니다
  2. 앞 장을 내보낸 시각을 빼면 이번 장의 프레임 시간입니다
  3. 장마다 나온 값을 그래프로 늘어놓거나 가장 오래 걸린 몇 퍼센트만 따로 봅니다

대가는 다루기 번거롭다는 것입니다. 한 장마다 값이 하나씩 나와 1초에도 수십 개가 쌓입니다. 한 줄로 줄이려면 평균이 아니라 오래 걸린 쪽 값을 골라 적어야 합니다.

상세

마라톤 완주 기록 한 줄만 보면 어느 구간에서 걸음이 무거워졌는지 알 수 없습니다. 1킬로미터마다 끊어 잰 구간 기록을 늘어놓으면 늦어진 구간이 바로 눈에 띕니다. 완주 기록이 괜찮아도 한 구간에서 한참 걸어간 주자도 가려낼 수 있습니다.

화면도 멈춘 그림을 한 장씩 이어 붙여 움직임을 만듭니다. 이 멈춘 그림 한 장이 프레임입니다. 프레임 시간(frame time)은 한 장을 화면에 내보낸 때부터 다음 장을 내보낼 때까지 걸린 시간입니다. 단위는 대개 밀리초입니다. 1초에 60장을 고르게 내보내는 프로그램이면 장마다 16.7밀리초씩 걸립니다.

이 값을 따로 재는 까닭은 매끄러움이 장 단위로 정해지기 때문입니다. 한 장이 늦게 나오면 그동안 화면은 앞 장에 멈춰 있습니다. 1초 동안 몇 장이 나왔는지만 세면 이 멈춤이 합계 속에 묻힙니다.

프레임률을 뒤집은 값

프레임률은 1초에 프레임이 몇 장 나오는지를 셉니다. 단위는 FPS(frames per second, 초당 프레임 수)입니다. 프레임 시간은 이 수를 뒤집은 것입니다. 1초, 곧 1000밀리초를 프레임률로 나누면 한 장당 시간이 나옵니다.

아래 표는 흔히 쓰는 프레임률마다 한 장에 걸리는 시간을 적은 것입니다.

프레임률 한 장당 시간 (밀리초)
30 FPS 33.3
60 FPS 16.7
120 FPS 8.3
144 FPS 6.9

표에서 보듯 둘은 반비례합니다. 프레임률이 두 배가 되면 한 장에 쓰는 시간은 반이 됩니다.

더하고 뺄 수 있는 단위

프레임 시간은 더할 수 있습니다. 한 장을 만드는 일이 입력 읽기, 상태 계산, 그리기로 나뉘면 각 일에 걸린 밀리초를 더해 그 장에 든 시간을 얻습니다. 기능 하나를 켜서 2밀리초가 늘면 어느 프로그램에서든 2밀리초가 느는 것입니다.

프레임률은 이렇게 더할 수 없습니다. 같은 2밀리초가 기준에 따라 전혀 다른 크기로 보입니다. 아래 표는 같은 일을 두 프로그램에 더했을 때입니다.

원래 프레임률 원래 한 장당 시간 2밀리초를 더하면 바뀐 프레임률
100 FPS 10.0 12.0 약 83 FPS
30 FPS 33.3 35.3 약 28 FPS

위쪽은 17장이 줄었습니다. 아래쪽은 2장이 줄었습니다. 드는 비용은 같습니다. 그런데 프레임률로 적으면 앞쪽 기능이 훨씬 비싸 보입니다.

그래서 성능을 견주거나 기능마다 비용을 적을 때는 프레임 시간을 씁니다. 한도도 밀리초로 잡습니다. 60 FPS 가 목표면 한 장에 쓸 수 있는 시간이 16.7밀리초입니다. 이 한도를 프레임 예산이라고 부릅니다. 한 장을 이루는 일들이 이 한도를 나눠 씁니다.

한 장을 재는 구간

화면을 그리는 프로그램은 한 바퀴에 한 장씩 내보내는 반복문 위에서 돕니다. 이 반복문을 게임 루프라고 합니다. 한 바퀴는 네 단계입니다.

  1. 입력을 읽습니다
  2. 상태를 고칩니다
  3. 그림을 그립니다
  4. 다 그린 그림을 화면에 넘깁니다

프레임 시간은 이 넘기는 순간과 다음 넘기는 순간 사이의 간격으로 잽니다. 아래는 그 방식을 줄인 코드입니다. 넘길 때마다 시각을 적고 앞 시각을 뺍니다.

prev = now()
while (running) {
  update(); render(); present();
  t = now()
  frameTime = t - prev  // 예: 16.7
  prev = t
}

present() 가 그림을 화면에 넘기는 호출입니다. frameTime 에 한 바퀴 동안 흐른 밀리초가 담깁니다. 이 간격은 화면에서 한 장이 머문 시간과 같아서 보는 사람이 느끼는 것에 가장 가깝습니다.

시각은 단조 시계로 잽니다. 단조 시계는 뒤로 가지 않는 시계입니다. 벽시계 시각은 시간 동기화로 갑자기 앞뒤로 옮겨질 수 있어 간격을 재는 데 쓰면 값이 튑니다.

간격과 일한 시간

간격 안에는 일하지 않고 기다린 시간도 들어갑니다. 모니터는 정해진 간격마다 화면을 새로 고칩니다. 1초에 60번 새로 고치는 모니터면 16.7밀리초마다 한 번입니다.

새 그림을 이 새로 고침에 맞춰 넘기도록 기다리는 설정이 있습니다. 이 설정에서는 일이 5밀리초에 끝나도 간격은 16.7밀리초로 나옵니다. 이때 프레임 시간은 16.7밀리초입니다. 이 설정은 아래 「고르게 나오는 것」에서 다시 봅니다.

장치가 그림을 만드느라 실제로 일한 시간은 따로 잽니다. 앞의 예에서는 5밀리초입니다. 간격은 보는 사람이 느끼는 것을 보여 줍니다. 일한 시간은 여유가 얼마나 남았는지를 보여 줍니다. 두 값은 쓰임이 다르니 무엇을 쟀는지 함께 적어야 합니다.

CPU 프레임 시간과 GPU 프레임 시간

한 장은 두 장치가 나눠 만듭니다. CPU(Central Processing Unit, 중앙 처리 장치)는 상태를 계산하고 무엇을 그릴지 명령 목록을 만듭니다. GPU(Graphics Processing Unit, 그래픽 처리 장치)는 그 목록을 받아 화면의 점 하나하나에 색을 칠합니다.

일한 시간도 장치마다 나눠 잽니다. CPU 가 한 장을 준비하느라 일한 시간이 CPU 프레임 시간입니다. GPU 가 한 장을 칠하느라 일한 시간이 GPU 프레임 시간입니다. 이름에 프레임 시간이 들어가지만 둘 다 간격이 아니라 일한 시간입니다.

두 장치는 동시에 일합니다. 아래 표는 시간을 구간으로 잘라 두 장치가 구간마다 하는 일을 적은 것입니다.

구간 CPU GPU
1 1번 장 준비 쉼
2 2번 장 준비 1번 장 칠하기
3 3번 장 준비 2번 장 칠하기

둘째 구간을 보면 GPU 가 1번 장을 칠하는 동안 CPU 는 벌써 2번 장을 준비합니다. 그러니 프레임 시간은 두 장치가 일한 시간을 더한 값이 아닙니다. 둘 가운데 더 오래 일하는 쪽이 프레임 시간을 정합니다.

GPU 쪽이 더 오래 걸려 프레임 시간이 늘어난 상태를 GPU 바운드라고 합니다. 이때는 CPU 일을 줄여도 프레임 시간이 거의 안 줄어듭니다. GPU 가 칠할 양을 줄여야 합니다.

칠할 양을 줄이는 흔한 방법이 컬링입니다. 화면에 보이지 않는 물체를 칠하기 전에 목록에서 뺍니다. 안 보이는 것을 칠하느라 GPU 시간을 쓰지 않게 됩니다.

CPU 쪽이 더 오래 걸리는 상태는 CPU 바운드라고 합니다. 이때는 GPU 일을 줄여도 프레임 시간이 거의 안 줄어듭니다. CPU 가 할 일을 줄여야 합니다.

CPU 일 가운데 흔히 줄이는 것이 드로 콜입니다. 드로 콜은 CPU 가 GPU 에 넘기는 그리기 명령 하나입니다. 명령 하나마다 CPU 가 챙길 일이 붙어서 수가 많으면 CPU 쪽 시간이 늘어납니다.

장치마다 나눠 재는 까닭이 이것입니다. 어느 쪽이 오래 걸리는지 모르면 엉뚱한 쪽을 줄이느라 시간을 씁니다.

평균이 가리는 끊김

1초 동안 59장은 16밀리초씩 걸리고 한 장만 56밀리초가 걸렸다고 합시다. 모두 더하면 1000밀리초에 60장이라 프레임률은 60입니다. 그런데 그 한 장을 기다리는 56밀리초 동안 화면은 멈춰 있었습니다.

보는 사람은 이 멈춤을 움직임이 툭 걸리는 느낌으로 받습니다. 이 현상을 끊김(stutter)이라고 부릅니다. 평균 프레임률만 보면 끊김이 있었는지조차 모릅니다.

프레임 시간을 장마다 늘어놓으면 바로 드러납니다. 아래는 그 1초 가운데 열 장을 막대로 세운 그림입니다. 가로는 몇 번째 장인지, 세로는 그 장에 걸린 밀리초입니다.

xychart-beta
    title "장마다 걸린 시간"
    x-axis "몇 번째 장" [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    y-axis "밀리초" 0 --> 60
    bar [16, 16, 16, 16, 16, 56, 16, 16, 16, 16]

여섯 번째 막대만 홀로 솟아 있습니다. 이 그래프를 프레임 시간 그래프라고 부릅니다. 게임 성능을 볼 때 흔히 여는 그림입니다. 막대가 낮고 고르면 매끄럽습니다. 솟은 막대가 곧 끊김입니다.

백분위수로 줄여 적기

그래프는 한눈에 보이지만 보고서에 한 줄로 적기는 어렵습니다. 오래 걸린 쪽 값을 골라 숫자 하나로 줄여 적습니다.

흔한 방법이 백분위수입니다. 프레임 시간을 짧은 것부터 줄 세웠을 때 99% 지점에 오는 값이 99번째 백분위수입니다. 100장이면 이보다 오래 걸린 장이 한 장뿐인 시간입니다.

가장 오래 걸린 1% 만 따로 모아 평균을 내는 방법도 흔합니다. 100장 가운데 가장 오래 걸린 한 장이 56밀리초면 그 평균은 56밀리초입니다. 이 값을 프레임률로 바꾸면 약 18 FPS 로 적힙니다. 평균 60 FPS 옆에 이 18을 나란히 두면 끊김이 숫자로 보입니다.

백엔드에서 응답 시간을 평균 대신 백분위수로 보는 것과 같은 까닭입니다. 드물게 오래 걸린 요청이 꼬리 지연이듯 드물게 오래 걸린 프레임이 끊김입니다. 평균이 낮아도 꼬리가 길면 사용자는 그 꼬리를 기억합니다.

고르게 나오는 것

프레임 시간은 짧기만 해서는 모자라고 고르게 나와야 합니다. 평균이 똑같이 16.7밀리초여도 16.7이 줄곧 이어지는 것과 10과 23.3이 번갈아 오는 것은 다르게 보입니다. 뒤쪽은 움직임이 빨라졌다 느려졌다 하며 떨려 보입니다.

화면이 그림을 받는 때도 정해져 있습니다. 모니터가 1초에 화면을 새로 고치는 횟수를 주사율이라고 합니다. 단위는 Hz(hertz, 헤르츠)입니다. 60 Hz 모니터는 16.7밀리초마다 한 번 화면을 새로 고칩니다.

수직 동기화를 켜면 새 그림은 이 새로 고침 사이에만 바꿔 끼웁니다. 화면에 한 장이 머무는 시간은 16.7, 33.3, 50처럼 16.7의 배수로만 나옵니다. 한 장이 16.7밀리초를 조금만 넘겨도 다음 새로 고침까지 기다려 33.3밀리초 동안 앞 장이 보입니다.

이렇게 장마다 머무는 시간을 고르게 맞추는 일을 프레임 페이싱이라고 부릅니다. 프레임 시간 그래프가 한 줄로 평평하면 페이싱이 고른 것입니다.

프레임 시간을 보는 때

게임과 3차원 그래픽 도구처럼 장마다 그림을 새로 그리는 프로그램이 이 값을 가장 많이 봅니다. 쓰는 때는 셋입니다. 기능 하나의 비용을 잴 때, 끊김이 난 장을 찾을 때, 프레임 예산을 나눌 때입니다. 웹 브라우저의 애니메이션과 앱의 화면 스크롤도 같은 방식으로 잽니다.

녹화된 영상을 트는 일에서는 볼 것이 적습니다. 영상은 한 장이 화면에 머물 시간이 파일에 이미 정해져 있어 장마다 새로 계산할 일이 없습니다.

사람에게 한 줄로 알릴 때는 여전히 프레임률이 익숙합니다. 「60 FPS」가 「16.7밀리초」보다 먼저 와닿기 때문입니다. 겉으로는 프레임률을 적습니다. 원인을 찾고 비용을 견줄 때는 프레임 시간으로 내려가는 경우가 많습니다.

관련 항목

프레임 시간을 뒤집거나 줄여 적는 지표

프레임률 · 백분위수 · 꼬리 지연 · 히스토그램

프레임 시간이 길거나 들쭉날쭉해서 생기는 화면 결함

스터터링 · 프레임 드롭 · 티어링 · 입력 지연 · 저더

프레임 시간을 화면 새로 고침에 맞추는 기술

수직 동기화 · 주사율 · 가변 주사율 · 프레임 페이싱 · 더블 버퍼링 · 트리플 버퍼링

프레임 시간을 나눠 쓰는 반복 구조와 단계

게임 루프 · 프레임 예산 · 델타 타임 · 고정 타임스텝 · 렌더 파이프라인 · 렌더 패스 · 프레임버퍼

프레임 시간을 늘리는 병목과 줄이는 기법

CPU 바운드 · GPU 바운드 · 드로 콜 · 오버드로 · 필레이트 · 컬링 · 오클루전 컬링 · 해상도 스케일링 · LOD

프레임 시간을 재는 도구와 시계

프로파일러 · 단조 시계 · 타임스탬프 · requestAnimationFrame · CPU · GPU

프레임 시간을 목표로 삼는 상위 분야

프레임 · 렌더링 · 실시간 렌더링 · 게임 개발 · 그래픽스 · 애니메이션

다른 이름: frame time · frametime · 프레임 타임 · 프레임당 시간