본문 바로가기
Win32 API/기초

Win32 API 기초 : Double Buffering

글: 시플마 2024. 6. 30.

윈도우에 사각형을 그리고 키 입력을 통해

움직일 수 있게 하였지만 잔상이 남는 문제가 있었습니다.

 

사각형을 계속 그리기만 하기 때문인데요, 

이 문제를 해결하려면 사각형을 지운 후 바뀐 위치에

사각형을 다시 그리고, 다시 키 입력이 들어오면 사각형을

지우고 다시 그리는 식으로 진행되어야 합니다.

 

그래서 CCore 클래스로 만든 코어 객체의

progress 함수가 실행되면서 호출되는 render 함수에서

 

73번째 줄을 보시면 Rectangle 함수 한 개가 

추가된 것을 확인할 수 있습니다.

 

이처럼 작업 영역에 딱 맞는 큰 사각형 하나를 그리면

기본 브러쉬는 흰색이므로 작업 영역이 모두 흰색으로

칠해질 것이고 마치 모든 영역이 지워진 것 같은

효과를 볼 수 있을 것입니다.

 

이때 좌상단 좌푯값을 -1로 설정하고 

우하단 좌푯값을 해상도에 +1한 값으로 하였습니다.

 

이유는 사각형을 그리는 함수이기 때문에 

검은색인 기본 펜으로 사각형의 테두리를

그리게 될 것입니다.

 

이 테두리가 작업 영역에 나오지 않도록 

작업 영역으로부터 1 픽셀 벗어난 곳에서

사각형을 그리는 것이죠.

 

 

이후 프로그램을 실행해 보면 

사각형을 움직일 시, 잔상이 남는 문제는 해결했지만

화면이 깜빡거리는 문제가 생긴 것을 볼 수 있습니다.

 

이러한 문제가 발생하는 이유는

제 컴퓨터 기준, 초당 2만 프레임 이상 

화면에 출력되고 있는데 이 말은 초당 2만 번 이상

사각형이 그려지고 지워지는 것을 반복하고 있다는 것이죠.

 

그래서 화면이 깜빡거리는 것처럼 보이는 겁니다.

 

 


 

 

 

윈도우에서는 사각형으로 이루어진 픽셀의 배열에

대한 정보를 비트맵이라는 개체에 저장합니다.

 

이러한 비트맵을 한 개 만들어서 그곳에 사각형을 그리고 

완성된 사각형을 띄워진 윈도우에 출력합니다.

 

이후 위치가 변경되면 비트맵에서 사각형을 지우고

변경된 위치에 사각형을 그린 후에 그 장면을

띄워진 윈도우에 출력합니다.

 

이런 식으로 하면 오직 사각형이 완성된 장면만

출력되므로 깜빡임이 사라지겠죠?

 

 

비트맵을 구성하기 위해 우선 

 

CCore 클래스에 HBITMAP형 멤버 m_hBit와

HDC형 멤버 m_memDC를 추가했습니다.

 

m_hBit에는 비트맵을, 

m_memDC에는 비트맵에

사각형을 그리기 위 DC를 저장하겠습니다.

 

위 두 개의 멤버들은

 

CCore 클래스의 초기화 함수인

Init 함수가 호출될 때 같이 초기화해 주겠습니다.

 

 

46번째 줄에서 CreateCompatibleBitmap 함수를 통해 

m_hBit에 비트맵을 저장해 주고 있습니다.

 

첫 번째 인자로 메인 윈도우의 DC를 넘기고 있습니다.

 

지금 만드는 비트맵은 결국 메인 윈도우에 출력하기 위한

비트맵이죠? 그렇기 때문에 메인 윈도우와 호환성을 맞추기 위해

넣는 것이라고 생각하면 됩니다.

 

2, 3번째 인자로 비트맵의 크기를 설정해 주고 있습니다. 

 

메인 윈도우와 똑같은 크기의 비트맵을 만듭시다.

 

 

47번째 줄에서 CreateCompatibleDC 함수를

통해서 새로운 DC를 만들고 있습니다.

 

새로운 DC를 만드는 이유는 비트맵을 만들었으면

그곳에 사각형을 그려야 합니다. 윈도우 구조상

비트맵에 바로 접근해서 사각형을 그릴 수 없으며,

기존의 DC는 메인 윈도우를 가리키고 있으므로 

새로운 DC를 하나 더 만들어 그 DC를 통해

비트맵에 사각형을 그리는 것이죠.

 

 

근데 새로 만든 DC와 비트맵이

서로 연결되어 있지 않습니다.

 

이러면 DC를 통해 비트맵에

사각형을 그리는 것이 불가능하죠.

 

48번째 줄에서 SelectObject 함수를 통해

새로 만든 DC가 저장된 m_memDC가 

새로 만든 비트맵을 저장한 m_hBit를 

가리킬 수 있도록 합니다.

 

이때 SelectObject 함수는 기존에 DC가

가리키고 있던 것을 반환합니다.

 

DC는 새로 만들면 기본적으로

1 픽셀의 비트맵을 가리키고 있습니다.

 

이 DC가 새로 만든 비트맵을 가리키게 할 것이므로

기존에 가리키고 있던 1 픽셀의 비트맵은 필요가 없죠.

 

그래서 HBITMAP형 변수 hOldBit에

기존에 가리키고 있던 1 픽셀의 비트맵을 반환받아

저장하고 이를 DeleteObject 함수를 통해 제거합니다.

 

 

 


 

 

이제 비트맵에 그려진 사각형을 

 

메인 윈도우에 출력하도록 하겠습니다.

 

93번째 줄에서 BitBlt 함수를 통해 메인 윈도우에

새로 만든 비트맵 데이터를 출력하고 있습니다.

 

첫 번째 인자는 비트맵의 데이터가

출력될 목적지를 의미하죠. 메인 윈도우의

DC를 넘깁니다.

 

2, 3번째 인자를 통해 복사받은 비트맵

데이터를 목적지의 어디 좌표부터

붙여 넣을지 선택합니다.

 

메인 윈도우의 좌상단 ( 0, 0 ) 위치부터 

비트맵 데이터를 출력하도록 합니다.

 

4, 5번째 인자는 복사받은 비트맵 데이터를

붙여 넣을 범위입니다. 전체 해상도만큼 붙여 넣도록 하죠.

 

6번째 인자는 소스, 즉 복사할 비트맵 데이터를 의미합니다.

 

비트맵의 DC를 인자로 넘깁니다.

 

7, 8번째 인자는 비트맵의 어디 좌표부터

복사할 것인지 설정합니다. ( 0, 0 ) 좌푯값에

존재하는 픽셀부터 복사를 하도록 합시다.

 

9번째 인자는 복사 옵션입니다.

 

SRCCOPY를 인자로 주면 새로 만든 비트맵의

데이터를 그대로 목적지에 붙여 넣게 됩니다.

 

 

이후 프로그램을 실행해 보면

 

잔상과 깜빡임 현상이 발생하지 않으며

사각형이 잘 움직이는 모습을 확인할 수 있습니다.

 

그런데 FPS가 상당히 떨어진 것을 확인할 수 있습니다.

 

매 프레임마다 비트맵에 사각형을 그리고

지우고 다시 그리고 있으며, 이 비트맵 데이터를

메인 윈도우에 복사하여 출력하고 있기 때문입니다.

 

단순한 동작이지만 CPU가 한 픽셀씩 복사하여

붙여 넣고 있으므로 상당히 많은 반복이 발생할 겁니다.

 

이러한 동작을 처리하기 위해

등장한 장치가 바로 GPU입니다.

 

DirectX에서는 GPU를 제어하는 함수를 통해

이러한 동작들을 GPU가 빠르게 처리할 수 있도록

할 수 있지만 윈도우의 렌더링 관련 함수는 CPU를

활용한 함수이므로 프레임이 떨어질 수밖에 없죠.

 

그렇다고 화면에 출력할 오브젝트를 추가할 때마다

FPS가 쭉쭉 떨어지느냐하면 또 그렇지는 않습니다.

 

이미 BitBlt 함수를 통해 비트맵 데이터의 픽셀을

하나씩 메인 윈도우에 복사하고 있기 때문에 

다른 오브젝트를 추가한다고 해도 색을 다르게

복사하기만 하면 될 뿐, 크게 더 떨어질 일은 없죠.

 

 


강의 출처 : https://www.youtube.com/watch?v=6Ai6iQeAr7k


'Win32 API > 기초' 카테고리의 다른 글

Win32 API 기초 : Key Manager (2)  (2) 2024.07.03
Win32 API 기초 : Key Manager (1)  (0) 2024.07.01
Win32 API 기초 : Timer (2)  (0) 2024.06.29
Win32 API 기초 : Timer (1)  (0) 2024.06.27
Win32 API 기초 : Core 클래스 (2)  (0) 2024.06.26