왜 Triple buffering을 사용하는가

지난번 글에서 double bufferingVSync에 관하여 설명하였다. 이번 글에서는 VSync가 가지는 문제와 그것을 어떻게 triple buffering이 해결하는지를 적어보도록 하겠다.

우선 다음 문제를 풀어보자.

vertical interval이 16.6ms마다 있는 display를 사용하고
double buffering을 사용하는 드라이버에서 VSync를 사용한다고 했을 때
back buffer에서 scene 하나를 완성하는 데 걸리는 시간이 16ms인 프로그램을 작성하면
이 프로그램은 몇 fps가 나올 것인가?

매 frame마다 하나의 scene을 16ms 동안 그리고, 남은 0.6ms는 VSync를 기다리기 때문에 16.6ms마다 한 frame을 그려서 60fps가 나온다. VSync를 기다리는 0.6ms가 아쉽기는 하지만 어차피 60fps 이상은 사람 눈으로 잘 구분이 안 되니 큰 손해를 보는 것은 아니다. 문제는 아래의 상황이다.

vertical interval이 16.6ms마다 있는 display를 사용하고
double buffering을 사용하는 드라이버에서 VSync를 사용한다고 했을 때
back buffer에서 scene 하나를 완성하는 데 걸리는 시간이
홀수 번째 scene은 15ms, 짝수 번째 scene은 17ms가 걸리도록 프로그램을 작성하면
이 프로그램은 몇 fps가 나올 것인가?

매 프레임 back buffer에 그리는 데 걸리는 평균 시간은 첫 번째 경우와 같다. 그렇다면 이 케이스에서도 60 fps가 나올까? 아니다. 홀수 번째 frame에서는 15ms 동안 그림을 그리고 1.6ms 동안 기다리지만, 짝수 번째 frame에서는 back buffer에 그림을 그리는데 17ms가 걸렸기 때문에 첫 번째 vertical interval 때 swap buffer를 하지 못하고, 그 다음 vertical interval까지 기다려야 한다. 즉, 그림이 완성된 뒤 16.2ms만큼 더 기다려야 화면을 바꿀 수 있다는 것이다.

이렇게 되면 frame 2개를 그리는데 3번의 vertical interval이 필요하므로, 약 40fps가 나오게 된다. 그림을 그리는 데 걸리는 평균 시간이 같은데도 성능은 2/3로 떨어지게 되는 것이다.

아래의 상황도 한번 보자.

vertical interval이 16.6ms마다 있는 display를 사용하고
double buffering을 사용하는 드라이버에서 VSync를 사용한다고 했을 때
back buffer에서 scene 하나를 완성하는 데 걸리는 시간이 17ms인 프로그램을 작성하면
이 프로그램은 몇 fps가 나올 것인가?

첫 번째 상황에 비해 scene 하나를 그리는 데 걸리는 시간이 고작 1ms 늘었을 뿐이다. 하지만 실제로 성능은 하나의 scene을 그리는데 2번의 vertical interval이 필요로 하므로, 원래 성능의 절반 정도인 30fps가 나오게 된다.

그러면 무조건 vertical interval 사이에 한 frame을 그리도록 프로그램을 작성해서 문제를 해결하면 안 될까?

음.... 가능하면 그렇게 하는 것이 좋다. 하지만 프로그램의 실행 시간이라는 것은 기본적으로 환경에 많은 영향을 받기 때문에 예측하기 어렵다. 가능하면 이 현상(이 현상에 이름이 있는지는 모르겠다.)의 근본적인 원인을 해결하는 것이 좋다. 그래서 해결책으로 나온 것이 Triple buffering이다.

Triple buffering은 기본적으로 Double buffering이랑 같지만 2개의 back buffer와 front buffer, 총 3개의 buffer를 사용한다. Triple buffering이 어떻게 문제를 해결하는지 이해하기 쉽게 해주는 다음 그림을 보자. 아래는 copy를 하는 경우만 나와 있지만, vsync를 사용한다면 copy든 flip이든 vertical interval 동안만 update가 가능하므로 flip과 큰 차이는 없다.

https://en.wikipedia.org/wiki/Multiple_buffering

위 그림의 4번 시나리오는 Double buffering과 vsync를 사용하는 경우 중 한 scene을 그리는 시간이 한 frame rate보다 길면 어떻게 되는가를 보여주는 자료다. draw B가 끝나는 부분을 자세히 보자. 아쉽게도 draw B는 한 프레임을 약간 넘어가 vertical interval 동안 화면을 갱신하지 못하게 되었다. 이렇게 타이밍을 놓친 scene은 그다음 vertical interval이 되어야 화면에 보일 수 있다. 그전까지는 여전히 draw A에서 그렸던 장면이 화면에 남아 있게 된다. 이것이 위에서 말했던 성능 저하의 원인이다.

다시 위 그림에서 5번 시나리오를 보자. 이건 4번과 똑같은 일을 triple buffering을 사용하여 처리한 것이다. double buffer에서는 draw Abuffer 1에서 video memory로 옮겨진 뒤 Vertical interval이 될 때까지 draw B가 시작하지 못했지만, triple buffering을 사용하는 경우 여분의 buffer가 하나 더 있기 때문에 draw B를 바로 시작할 수 있다.

이렇게 식으로 triple buffering을 이용하면 VSync로 인한 성능 저하를 줄일 수 있다. 하지만 surface만큼의 메모리를 더 사용해야 하는 단점이 있어서 아직 대부분의 그래픽 카드에서 기본으로 지원하는 기능은 아니다. 하지만 새로 나오는 그래픽 카드들에는 그래픽 카드에서 지원하는 기능으로 들어가기 시작했고, 지원 하지 않는 그래픽 카드에서는 FBO를 이용하여 구현할 수 있다.

댓글

댓글 쓰기

이 블로그의 인기 게시물

[C++] enum class - 안전하고 쓰기 쉬운 enum

RAII는 무엇인가

Log Aggregator 비교 - Scribe, Flume, Fluentd, logstash

[Python] cache 데코레이터로 최적화하기

[Web] SpeechSynthesis - TTS API