이 글은 Vulkan을 학습하는 개발자의 관점에서, GLFW를 사용하여 Vulkan 렌더링을 위한 창을 생성하고 이를 Vulkan 인스턴스와 연결하여 VkSurfaceKHR를 만드는 과정을 사실 위주로 정리한 기록입니다.
1. GLFW 창 생성 시 주의점
Vulkan 렌더링에 사용할 창을 생성할 때는, GLFW가 자체적으로 OpenGL 컨텍스트를 생성하지 않도록 막아야 합니다. 만약 이 과정이 생략되면 Vulkan과 충돌이 발생합니다. 따라서 glfwCreateWindow를 호출하기 전에 아래와 같이 힌트를 설정해 주어야 합니다.
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
이 설정을 통해 GLFW는 순수하게 창 관리 역할만 수행하게 됩니다.
2. Vulkan 인스턴스와 창 연결: VkSurfaceKHR
VkSurfaceKHR 객체는 플랫폼에 종속적인 창과 플랫폼 독립적인 Vulkan을 연결합니다. 이 객체는 glfwCreateWindowSurface 함수를 통해 생성합니다.
glfwCreateWindowSurface(instance, window, pAllocator, &surface);
이 함수는 Vulkan 인스턴스(instance)와 GLFW 창 포인터(window)를 받아, Vulkan이 렌더링 대상으로 인식하는 표준화된 표면(surface)을 만들어 반환합니다. 이 함수를 호출하기 위해서는 VkInstance 생성 시 glfwGetRequiredInstanceExtensions를 통해 필요한 확장 기능들을 활성화해야 합니다.
3. 물리 디바이스 선택에서의 VkSurfaceKHR의 역할
VkSurfaceKHR는 단순히 그림을 그릴 표면을 지정하는 것 이상의 중요한 의미를 가집니다. 어떤 물리 디바이스(GPU)를 사용할지 결정하는 과정에 필수적인 기준이 됩니다. 시스템에 여러 개의 GPU가 장착된 경우, 모든 GPU가 현재 화면에 그림을 출력하는 기능을 지원하지는 않습니다.
따라서 vkGetPhysicalDeviceSurfaceSupportKHR 함수를 사용하여, 우리가 생성한 VkSurfaceKHR에 실제로 렌더링 결과를 출력할 수 있는 능력을 갖춘 GPU를 찾아야 합니다.
4. 창 크기 변경 처리
사용자가 창의 크기를 변경하면, 스왑체인을 포함한 여러 Vulkan 리소스를 모두 다시 생성해야 합니다. 이는 비용이 큰 작업이므로, 매 프레임 창 크기를 검사하는 방식은 비효율적입니다.
대신 GLFW의 콜백 함수(glfwSetFramebufferSizeCallback)를 사용하여 창 크기 변경 이벤트를 감지하고, 이때 플래그 변수(예: framebufferResized)를 true로 설정합니다. 렌더링 루프에서는 이 플래그를 확인하여 리소스 재생성 여부를 결정합니다.
또한, vkAcquireNextImageKHR 또는 vkQueuePresentKHR 함수가 VK_ERROR_OUT_OF_DATE_KHR 오류를 반환하는 경우도 창 상태가 변경되었음을 의미하므로, 동일하게 리소스 재생성 작업을 수행해야 합니다.
5. 리소스 소멸 순서
Vulkan 프로그래밍에서는 리소스를 생성한 순서의 역순으로 소멸시켜야 합니다. VkSurfaceKHR는 VkInstance에 종속된 객체이므로, 반드시 VkInstance보다 먼저 소멸되어야 합니다.
따라서 일반적인 정리 순서는 다음과 같습니다.
vkDestroySurfaceKHR로 서피스 소멸vkDestroyInstance로 인스턴스 소멸glfwDestroyWindow로 창 소멸glfwTerminate로 GLFW 종료
이러한 의존성 관계와 소멸 순서를 지키는 것은 메모리 누수와 예기치 않은 오류를 방지하는 데 매우 중요합니다.
이 게시물은 학습한 내용을 바탕으로 초안을 작성한 뒤, LLM의 도움을 받아 내용을 검수하고 다듬어 완성되었습니다.