[GPU] Double Buffering, Triple Buffering, and VSync

GPU (그래픽스)에서 설명하는 Buffer라는 단어는 GPU (컴퓨터)가 연산을 마무리하고 모니터에 Display되야 하는 데이터를 저장하는 공간을 의미한다. 예전에는 보통 1개의 Buffer를 사용하여 컴퓨터가 연산한 결과를 Buffer에 작성함과 동시에 모니터에 Display 하는 작업을 수행하였다. 1개의 Buffer를 사용하면 Flicker, Tearing과 같은 문제가 있어서 Double, Triple Buffers, VSync 기술을 사용하여 문제를 해결하였다.

Buffering 예제

위키피디아 (출처1)에 Buffering에 대한 예제 설명이 잘되어있다. 같은 예제를 들어서 설명을 해보겠다. 가정으로 내가 아주 큰 집에서 살고 있다. 이번에 날씨가 너무 더워 외부에 설치 가능한 수영장을 하나 구매해서 설치를 완료했다. 그런데 호스가 없어서 물을 채울 방법은 내가 화장실을 가서 수도꼭지를 열어서 물 통에 물을 채운 다음 수도꼭지를 잠그고 수영장까지 걸어와서 물통 물을 수영장에 붓는 행위를 반복해야 한다. 지금 설명한 방법이 하나의 Buffer를 사용한 경우를 보여준다.

만약 물통 하나가 더 있으면 조금 더 효율적으로 수영장에 물을 채울수 있다. 물통 하나는 화장실에서 수도꼭지를 열어서 물을 채우고 있고, 그 동안 채워진 물통을 수영장에 가져가서 물을 채우면된다. 물을 모두 부어서 물통이 비면 화장실로 가서 빈 물통에 물을 다시 채우는 동안 다른 물통을 수영장으로 가져간다. 이렇게 하면 하나의 물통을 사용하는 경우보다 조금더 효율적이다. 이 경우가 Double Buffering의 예제이다. 여기서 만약 한사람과 물통 하나가 더 있다고 가정을 해보자. 2 사람은 물통을 계속 나르고, 1개는 계속 화장실에서 물을 채우는 것 입니다. 이 경우는 Triple Buffer을 사용하는 경우이다. 위 예제는 정말 현실성 없는 예제이다. 하지만 Buffering에 대한 좋은 예제인것 같다.

A Buffer (Single Buffer라는 단어를 사용하지는 않는 것 같다)

그림 1: 하나의 Buffer 예제

예전의 컴퓨터는 하나의 Buffer만은 사용했다고 한다. (도대체 얼마나 예전을 의미하는지는 모르겠다) 이 경우 컴퓨터 (GPU) 가 연산을 하고 Buffer에 데이터를 쓰는 동안 모니터가 해당 데이터를 Display 하는 것을 동시에 수행한다. 이 경우 Display에 데이터값이 출력되는 동안 Buffer의 값이 달라질 수 있고, 깜빡거림과 같은 문제가 발생할 수도 있다. 그림 1은 하나의 버퍼를 사용하는 예제이다. 출처 2에 따르면 하나의 Buffer를 사용하는 경우에 이득이 있다고 설명을 하는데 정확한 설명은 찾지 못하였다. 그래서 생각해본 것인데 만약 컴퓨터 그래픽 연산 성능이 모니터 화면의 Refresh 속도와 같으면 하나의 Buffer로도 문제가 없을 것 같다. 이 경우 메모리 저장공간과 복사에 따른 Overhead가 없을 것 같긴 하다 (이 생각은 글을 읽으면서 개인적으로 생각한 부분이다. 정확하게 맞는 설명인지는 잘 모르겠다)

Double Buffering

그림 2: Double Buffering 예제

하나의 Buffer를 사용하여 쓰고/읽기를 반복하면 깜빡거림 (Flicker) 현상이 발생한다. 이 문제를 해결하기 위해 Buffer를 하나 더 추가한다. 하나의 Buffer는 컴퓨터 (GPU) 가 연산한 값을 저장하는 Buffer이고, 다른 하나는 모니터가 Display 하기 위한 읽기 전용 Buffer이다. Write 하는 Buffer를 보통 Back Buffer라고 하고, 모니터가 읽고 있는 Buffer를 Front Buffer라고 한다. 이렇게 2개의 Buffer를 사용하면 Swap이라는 동작이 필요하다. GPU 연산 결과가 Back Buffer에 저장되면, Front Buffer와 교체를 하여 모니터에 결과를 출력한다. 이렇게 Buffer를 교환하는 동작을 Swap이라고 한다. Swap을 하게 되면 Front Buffer는 Back Buffer가 되고 Back Buffer는 Front Buffer가 된다. 그림 2는 Double Buffering의 예제이다.

VSync (수직동기화): Synchronizing buffers swaps with the Vertical refresh (or Vertical Synchronization) 

그림 3: Double Buffering + VSync 예제

단순히 Double Buffering만 사용하는 경우 Swap 동작은 언제든지 발생할 수 있다. 다시 말해서 모니터가 Front Buffer에서 데이터를 읽어서 출력하는 동안 Back Buffer와 교체(Swap)가 될 수도 있다. 데이터값을 모니터에 출력하는 중간에 Swap이 발생하면 이미지 Tearing 문제가 발생하게 된다. 이미지 Tearing은 모니터 출력된 그림이 찢어지는 현상을 의미한다. 출처3에서 VSync와 Tearing 현상을 잘 설명하고 있다. Tearing 문제를 해결하기 위해 Vsync를 사용한다. VSync는 Swap 동작을 모니터의 Refresh 주기에 맞춰서 Swap 동작을 수행하는 것이다. 모니터는 보통 1초에 60개의 이미지를 출력한다 (Typically 60Hz for most LCD panels). 모니터 Refresh 주기 (Rate)에 맞춰서 Swap 동작을 수행하면 이미지 Tearing 현상을 해결할 수 있다. 그림 3은 Double Buffering과 Vsync의 예제이다.

Triple Buffering

그림 4: GPU 연산이 16.66ms 이전에 완료되는 경우

그림 5: GPU 연산이 16.66ms 이상 필요한 경우

Double Buffering과 VSync(수직동기화)를 사용해서 Flicker, Tearing 문제를 해결하였다. 하지만 Double Buffering과 VSync를 사용하는 경우 GPU (컴퓨터) 성능대비 FPS 성능이 급격하게 떨어지는 현상이 발생 할 수 있다. 모니터가 초당 60개의 이미지를 출력하면 1개의 이미지 (프레임)을 출력하는 시간은 대략 16.66ms이다. 만약 GPU (컴퓨터)가 하나의 이미지를 계산하는데 대략 17ms가 걸린다고 하면 GPU 성능은 2% 하락하였지만, FPS 성능은 절반 (30FPS)으로 줄어든다. 그림 4는 16.66ms 안에 모든 연산을 완료하여 모니터 Refresh 주기마다 Swap 동작을 하여 새로운 이미지를 출력하는 예제이다. 반면 그림 5는 GPU 연산이 완료되지 않아서 2번의 주기에 한 번씩만 새로운 이미지가 출력되는 예제이다. Buffer가 2개만 존재하지 때문에 Free Buffer가 없으면 GPU는 새로운 연산 결과를 저장할 수 있는 공간이 없다.

그림 6: Triple Buffering 예제

문제를 해결하기 위해 단순히 Buffer를 하나 더 추가한 Triple Buffering을 사용한다. 그림 6은 Triple Buffering 예제이다. 2개 Back Buffer와 1개의 Front Buffer가 존재한다. 3개의 버퍼를 사용하는 경우 위와 같이 17ms 정도 걸리는 연산을 출력하는데 30 FPS보다 높은 성능을 낼 수 있다. 한 개의 추가 Buffer가 존재하기 때문에 Back Buffer가 Front Buffer로 바뀌지 않아도 다른 Back Buffer에 결과를 작성 할 수 있다. Triple Buffer를 사용하는 경우 약간의 Delay가 발생 할 수 있다. 하지만, 이 Delay는 크게 느껴지지 않는다고 한다. 이와 더불어 보통 하나의 Buffer는 15~25MB로 알려져 있다. 요즘 같이 4~8GB 메모리는 사용하는 GPU에서 25MB는 무시 가능한 수치이다.

Quad (or Six) Buffering

Quad Buffering은 말 그대로 4개의 Buffer를 사용하는 것이다. 보통 Quad Buffering은 3D 영상과 같이 오른쪽 왼쪽 이미지를 따로 생성할 때 사용한다. 다시 말해서 왼쪽 이미지 연산에 2개의 Buffer, 오른쪽 이미지 연산에 2개의 Buffer를 사용한다. Six Buffering은 왼쪽 오른쪽 이미지에 각 Triple Buffering을 사용한 경우이다.

추가 설명

Buffer 데이터를 Swap 하는 방법은 Back Buffer를 Front Buffer로 복사 후 Front Buffer 메모리를 Free 하는 방법이 있다 (보통 Software Approach인 것 같다). 또 다른 방법은 모든 Buffer가 VRAM (Video RAM)에 존재하여 출력할 수 있도록 설계합니다. 그리고 단순히 Pointer 시작 값을 Buffer의 시작 메모리 주소로 변경하는 방법이 있다. 이를 Page Flipping이라 한다. 당연히 Page Flipping 방법이 더 효율적인 것 같다. 또한, 안드로이드 기기는 Triple Buffering을 사용한다. 대부분의 안드로이드 기기는 초당 60개의 이미지 (60 FPS)를 출력한다. 요즘 게임용 모바일 기기로 출시되는 제품은 90~120 FPS를 지원하기도 한다. (출처 4). 개인적으로 핸드폰에 60 FPS 이상이 필요한지 모르겠다.

출처

  1. https://en.wikipedia.org/wiki/Multiple_buffering
  2. https://www.anandtech.com/show/2794/2
  3. https://namu.wiki/w/%EC%88%98%EC%A7%81%EB%8F%99%EA%B8%B0%ED%99%94
  4. https://www.androidpolice.com/2017/11/01/razer-phone-hands-120hz-displays-phones-need-happen-immediately/

Leave a Comment