본문으로 건너뛰기
뒤로가기

[Vulkan] 커맨드 풀 설계: 스왑체인 기반과 프레임 기반 구조 비교 분석

이 글은 Vulkan을 학습하는 개발자의 관점에서, 커맨드 풀 설계를 위한 두 가지 접근 방식(스왑체인 이미지 기반과 프레임 인 플라이트 기반)을 비교하고 정리한 기록입니다.

[Vulkan] 커맨드 풀 설계: 스왑체인 기반과 프레임 기반 구조 비교 분석

Vulkan 튜토리얼을 따라 애플리케이션을 만들다 보면 자연스럽게 특정 구조를 사용하게 됩니다. 저 또한 스왑체인 이미지 개수에 맞추어 커맨드 버퍼를 생성하는 일반적인 방식으로 학습을 시작했습니다. 하지만 이 설계가 더 복잡한 상황에서는 비효율적이라는 점을 배우게 되어, 이를 개선하는 프레임 인 플라이트(Frame-in-Flight) 기반 설계와 비교하며 학습한 내용을 정리하고자 합니다.

1. 일반적인 학습 구조: 스왑체인 이미지 기반

대부분의 튜토리얼은 아래와 같은 리소스 구성을 기반으로 렌더링 파이프라인을 구축합니다.

항목개수설명
스왑체인 이미지3개화면에 표시하기 위한 이미지 버퍼
프레임 인 플라이트 (FIF)2개CPU가 GPU의 작업 완료를 기다리지 않고 미리 준비할 수 있는 프레임의 최대 개수
그래픽스 큐 패밀리1개모든 렌더링 명령을 제출하는 통로
커맨드 풀1개그래픽스 큐 패밀리에 종속된 커맨드 버퍼 할당자
커맨드 버퍼3개스왑체인 이미지 개수와 동일하게 생성

이 구조에서는 vkAcquireNextImageKHR을 통해 얻은 스왑체인 이미지의 인덱스를 사용하여, 해당 인덱스에 맞는 커맨드 버퍼에 렌더링 명령을 기록합니다.

2. 스왑체인 이미지 기반 구조의 특징

장점

단점

3. 개선된 구조: 프레임 인 플라이트 기반

이러한 문제들을 해결하기 위해, 각 프레임 인 플라이트(Frame-in-Flight)가 자신만의 리소스 집합을 소유하는 구조를 적용합니다.

struct FrameResources {
    VkCommandPool    commandPool;
    VkCommandBuffer  commandBuffer;
    VkFence          renderFence;
    VkSemaphore      imageAvailable;
    VkSemaphore      renderFinished;
};

// FIF 개수만큼만 리소스 묶음을 생성
FrameResources frames[MAX_FRAMES_IN_FLIGHT]; // 보통 2개

이 구조에서는 더 이상 스왑체인 이미지 인덱스를 커맨드 버퍼 선택에 사용하지 않습니다. 대신, 현재 CPU가 작업할 프레임(currentFrame)의 FrameResources를 가져와 사용합니다.

4. 프레임 인 플라이트 기반 구조의 장점

5. 동기화 과정의 이해

프레임 인 플라이트 기반 구조의 주된 동기화 흐름은 다음과 같습니다.

  1. vkAcquireNextImageKHR(): 렌더링할 스왑체인 이미지를 GPU로부터 받아옵니다. MAX_FRAMES_IN_FLIGHT가 스왑체인 이미지 개수보다 작다면, GPU가 사용 중이지 않은 가용 이미지가 확보될 가능성이 커 CPU의 대기 시간이 줄어듭니다.
  2. vkWaitForFences(): 현재 프레임(currentFrame)에 대한 작업을 시작하기 전, 이 프레임 슬롯을 사용했던 이전 렌더링(예: 2 프레임 전)이 GPU에서 완전히 끝났는지 Fence를 통해 확인하고 기다립니다. 이 과정이 CPU와 GPU의 파이프라인을 동기화하는 핵심적인 역할을 합니다.
  3. vkResetCommandPool(): 펜스를 통해 안전을 확인한 후, 이 프레임의 커맨드 풀을 리셋하고 새로운 명령 기록을 시작합니다.
  4. vkQueueSubmit(): 명령 기록이 끝난 커맨드 버퍼를 큐에 제출하고, 작업 완료를 추적할 Fence를 함께 전달합니다.

MAX_FRAMES_IN_FLIGHT = 2, 스왑체인 이미지 개수 = 3 구성은 CPU가 한 프레임을 준비하는 동안 GPU는 이전 프레임을 렌더링하고, 디스플레이는 그 이전 프레임을 보여주는 안정적인 파이프라인을 구축하는 표준적인 방식입니다.

6. 정리하며

스왑체인 이미지 기반 구조는 Vulkan의 기본 개념을 익히는 데 훌륭한 시작점입니다. 하지만 데이터가 복잡해지고 동적인 변화가 많은 애플리케이션을 목표로 한다면, 프레임 인 플라이트 기반 설계로 전환하는 것이 필수적이라는 결론을 내렸습니다. 이 구조는 단순히 메모리 효율성을 높이는 것을 넘어, 동기화 모델을 명확하게 하고 향후 멀티스레딩과 같은 고급 기법으로 확장할 수 있는 견고한 토대를 마련해 줍니다.


이 게시물은 학습한 내용을 바탕으로 초안을 작성한 뒤, LLM의 도움을 받아 내용을 검수하고 다듬어 완성되었습니다.


공유하기:

이전 글
[Vulkan] 프레임 렌더링을 위한 동기화 객체 생명주기 관리
다음 글
[Vulkan] MoltenVK 종료 시 메모리 할당 메시지 분석