이론/디자인패턴

이중 버퍼

tsyang 2022. 1. 26. 01:04

왜쓰나?

  1. 여러 순차 작업의 결과를 한 번에 보여주고 싶을 때.
  2. 변경 중인 상태에 접근할 수 없게 하고 싶을 때.
  3. 코드가 변경하려는 상태를 다시 읽는 경우.

 

이중 버퍼를 사용하는 가장 대표적인 예는 게임 렌더링이다. 

 

class FrameBuffer
{
public:
	void clear();	//픽셀을 모두 하얀색으로 채운다.
	void draw(int x, int y);	//x,y 픽셀을 검은색으로 바꾼다.
};

class Scene
{
public:
	void draw();
	FrameBuffer& getBuffer();

private:
	FrameBuffer buffer_;
};

이처럼 FrameBuffer와 화면에 흑백 그림을 그려주는 Scene이 있다고 하자.

 

Scene은 화면에 곰돌이 한 마리를 그린다.

 

void Scene::draw()
{
	buffer_.clear();
	
	//곰돌이를 그린다.
	
	//Part1 : 몸통 부분을 그린다
	buffer_.draw(1, 5);
	//계속..
	
	//Part2 : 머리 부분을 그린다.
	buffer_.draw(2, 2);
	//계속..
}

 

여기까지는 문제가 없어 보인다. 하지만 비디오 드라이버가 몸통을 그리는 부분과 머리를 그리는 부분 사이에 호출이 된다면?

 

void Scene::draw()
{
	buffer_.clear();
	//곰돌이를 그린다.
	
	//Part1 : 몸통 부분을 그린다

	// <===!! 비디오 드라이버가 buffer_를 읽어 화면을 렌더링한다.
	
	//Part2 : 머리 부분을 그린다.
}

화면에는 1프레임동안 머리가 없는 곰돌이가 그려지게 된다.

 

문제는 비디오 드라이버가 변경 중인 FrameBuffer에 접근했다는 것이다.

 

class Scene
{
public:
	void draw();
	FrameBuffer& getBuffer();	//current_를 리턴한다.

private:
	void swap();	//current_와 next_를 swap
	
	FrameBuffer buffers_[2];
	FrameBuffer* current_;
	FrameBuffer* next_;
};

이제 Scene은 두 개의 FrameBuffer를 가진다. swap은 단순히 포인터만 교체해주는 작업을 한다. 외부에서 프레임 버퍼에 접근할 때는 current_에만 접근할 수 있다.

 

void Scene::draw()
{
	next_->clear();

	//곰돌이를 그린다.

	//Part1 : 몸통 부분을 그린다
	next_->draw(1, 5);
	//계속..

	//Part2 : 머리 부분을 그린다.
	next_->draw(2, 2);
	//계속..

	//다 그렸으면 swap해준다.
	swap();
}

draw()에서는 next_ 버퍼에만 작업을 해준다. 외부에서는 current_에만 접근이 가능하므로 next_에는 접근이 불가능하다.

 

이렇게 되면 비디오 드라이버가 언제 버퍼를 읽어도 온전한 곰돌이를 볼 수 있다. 

 

위 예제에서는 단순히 포인터를 교체하였지만 상태에 포함되는 데이터가 작은 경우 데이터를 복사하여 swap을 해줄 수 있다.

 

 

주의사항

1. 교체 연산 자체에 시간이 걸린다 : 앞서 렌더링 예의 경우 단순히 포인터를 swap하는 것이기 때문에 충분히 빠르다. 그러나 버퍼에 값을 쓰는 것 보다 교체가 더 오래 걸린다면 이중 버퍼 패턴은 무용지물이다.

2. 추가적인 메모리가 필요하다 : 추가적인 버퍼를 마련해야 하기에 그만큼 메모리가 더 필요하다. 

3. 버퍼에 남은 데이터를 재사용 할 때 주의해야 한다 : 1프레임 전이 아니라 2프레임 전의 데이터를 들고 있다. (포인터 스왑 기준)

 

만약 이중 버퍼 패턴을 사용하지 못한다면 상태를 변경하는 동안 밖에서 접근하지 못하게 할 방법을 찾아야 한다.

 

참고 : 로버트 나이스트롬, 게임 프로그래밍 패턴, 한빛미디어, [143-159p]

'이론 > 디자인패턴' 카테고리의 다른 글

타입 객체  (0) 2022.02.10
게임 루프  (1) 2022.01.31
서비스 중개자  (0) 2022.01.16
이벤트 큐 패턴  (1) 2022.01.03
컴포넌트 패턴  (1) 2021.12.25