이 글은 Vulkan을 학습하는 개발자의 관점에서, Vulkan 초기화 과정, 특히 창(Window), 인스턴스(Instance), 디바이스(Device) 설정 흐름을 사실 위주로 정리한 기록입니다.
Vulkan 초기화 순서
Vulkan에서 리소스를 생성할 때는 각 단계가 이전 결과에 의존하므로, 정해진 순서를 따르는 것이 중요합니다.
1단계: 윈도우 생성 및 서피스 연결
가장 먼저 운영체제(OS)의 창을 만들고, 렌더링 결과물이 그려질 VkSurfaceKHR를 연결합니다. 이 VkSurfaceKHR는 Vulkan의 렌더링 결과와 OS의 화면을 연결하는 역할을 합니다.
- 구현 (GLFW 기준):
glfwCreateWindow로 창을 생성한 뒤,glfwCreateWindowSurface를 호출하여VkInstance와 창을 연결해VkSurfaceKHR를 만듭니다. - 주요 고려사항: HiDPI 디스플레이의 경우,
glfwGetFramebufferSize를 사용해 실제 렌더링에 사용될 해상도를 가져와야 합니다. 또한VkSurfaceKHR는VkInstance가 소유한 객체이므로, 인스턴스보다 먼저 파괴되어야 합니다.
2단계: 인스턴스 생성 및 밸리데이션 설정
VkInstance는 Vulkan API 사용을 위한 진입점입니다. 이 단계는 Vulkan 드라이버와 애플리케이션을 연결하는 과정입니다.
- 구현:
glfwGetRequiredInstanceExtensions를 통해 윈도우 시스템 통합(WSI)에 필요한 확장 목록을 가져옵니다. 여기에 디버깅을 위한VK_EXT_debug_utils나 macOS 호환성을 위한 확장 등을 추가할 수 있습니다.- 개발 중에는 API 오용을 감지하기 위해
VK_LAYER_KHRONOS_validation밸리데이션 레이어를 활성화하는 것이 좋습니다. vkCreateInstance를 호출하여 인스턴스를 생성합니다.- 이후
vkCreateDebugUtilsMessengerEXT를 호출하여 디버그 메시지를 수신할 콜백을 등록하면 디버깅에 큰 도움이 됩니다.
- 주요 고려사항: macOS에서 Vulkan을 실행할 때는
VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR플래그와 관련 확장을 설정해야 합니다. 이 설정이 없으면vkEnumeratePhysicalDevices가 사용 가능한 GPU를 찾지 못하는 문제가 발생합니다.
3단계: 물리 및 논리 디바이스 선택
시스템에 장착된 GPU(VkPhysicalDevice) 중에서 애플리케이션의 요구사항에 맞는 것을 고르고, 실제 연산에 사용할 VkDevice(논리 디바이스)와 커맨드를 전달할 VkQueue를 생성합니다.
- 구현:
vkEnumeratePhysicalDevices로 시스템의 모든 GPU 목록을 가져옵니다.- 각 GPU의 속성과 지원하는 기능을 확인하여 가장 적합한 디바이스를 선택합니다.
- 선택된 GPU에서 그래픽 작업과 화면 출력을 모두 지원하는 큐 패밀리(Queue Family)를 찾습니다.
vkCreateDevice를 호출하여 논리 디바이스를 생성합니다. 이때 스왑체인 같은 필수 확장과 함께 사용할 큐의 정보를 전달합니다.
- 주요 고려사항: 그래픽 작업을 처리하는 큐와 화면에 결과를 출력하는 프레젠트 큐가 서로 다른 큐 패밀리에 속하기도 합니다. 이 경우, 두 큐 사이에서 리소스를 공유하는 방식을 명시적으로 설정해야 합니다.
4단계: 애플리케이션 루프와 종료
모든 리소스가 준비되면, 창이 닫힐 때까지 렌더링 루프를 실행합니다. 루프가 종료된 후에는 리소스를 안전하게 해제하는 것이 매우 중요합니다.
- 구현:
while (!glfwWindowShouldClose(window))루프 안에서 렌더링 작업을 수행합니다.- 루프가 끝나면,
vkDeviceWaitIdle(device)을 호출하여 GPU가 진행 중인 모든 작업을 마칠 때까지 기다립니다. - 이후, 리소스를 생성했던 순서의 정확한 역순 으로 모든 Vulkan 객체를 파괴합니다.
생성 및 파괴 순서 요약
리소스 누수를 막기 위해서는 다음 순서를 지키는 것이 중요합니다.
- 생성 순서:
- Window (GLFW)
VkInstance- Debug Messenger (선택 사항)
VkSurfaceKHRVkDevice
- 파괴 순서 (생성의 역순):
vkDeviceWaitIdle(device)호출VkDeviceVkSurfaceKHR- Debug Messenger (생성했을 경우)
VkInstance- Window (GLFW)
학습 중 알게 된 추가 정보
- 디버깅:
VK_EXT_debug_utils확장을 사용하면vkSetDebugUtilsObjectNameEXT함수로 Vulkan 객체에 이름을 붙일 수 있습니다. 밸리데이션 레이어 오류 메시지에 이 이름이 표시되어 어떤 리소스에서 문제가 발생했는지 쉽게 파악할 수 있습니다. - 디바이스 선택: 단순히 사용 가능한 첫 번째 GPU를 선택하기보다, 외장 GPU(Discrete GPU)를 우선적으로 선택하는 로직을 구현하는 것이 더 나은 접근법이라는 것을 배웠습니다.
- 초기화 실패 처리:
vkCreateInstance나vkCreateDevice같은 함수는 실패하기도 합니다. 실패 시점까지 생성된 모든 리소스가 안전하게 해제되도록 에러 처리 로직을 구성해야 리소스 누수를 방지할 수 있습니다.
안정적인 초기화는 Vulkan 학습의 중요한 첫걸음입니다. 다음으로는 렌더링된 이미지를 화면에 표시하기 위한 스왑체인(Swapchain) 설정에 대해 학습할 계획입니다.
이 게시물은 학습한 내용을 바탕으로 초안을 작성한 뒤, LLM의 도움을 받아 내용을 검수하고 다듬어 완성되었습니다.