[모던 Vulkan (C++ 버전)] #2: 인스턴스 생성 (RAII와 예외 처리 활용)

모던 Vulkan (C++ 버전) 시리즈의 두 번째 글입니다. 이번 글에서는 인스턴스를 생성하는 과정을 Vulkan-HPP와 Modern C++ 스타일로 재작성해보겠습니다. 이전 입문 시리즈에서 C 스타일로 인스턴스를 만들고 Validation Layer를 설정했던 과정을 다시 돌아보며, 이번에는 RAII를 활용한 vk::UniqueInstance, 예외 처리 모드 등을 적용하여 더 깔끔하고 유지보수하기 쉬운 코드를 작성하는 방법을 제시합니다.

목표

  • Vulkan C API 스타일로 작성했던 인스턴스 생성 코드를 Vulkan-HPP 기반 C++ 코드로 재구성
  • vk::ApplicationInfo, vk::InstanceCreateInfo 등을 이용하여 Application, Engine, API 버전 정보 설정
  • vk::createInstanceUnique()를 통한 RAII 기반 인스턴스 관리
  • Validation Layer와 Extension 설정 시 C++ STL 컨테이너와 enum class 활용
  • 예외 처리 모드 사용 여부에 따른 에러 처리 전략 예시

기본 개념 복습

Vulkan 인스턴스 생성 시 해야 할 일:

  1. VkApplicationInfo를 통해 앱 정보(앱 이름, 엔진 이름, Vulkan 버전) 설정
  2. VkInstanceCreateInfo에 Validation Layer와 Extension 목록을 지정
  3. vkCreateInstance로 인스턴스 생성 후 필요 시 vkDestroyInstance 호출

C 스타일에서는 이러한 과정을 구조체 초기화와 함수 호출로 처리했습니다. 이제 Vulkan-HPP로 전환하면:

  • vk::ApplicationInfo, vk::InstanceCreateInfo와 같은 C++ 클래스로 구조체 초기화 대체
  • vk::createInstance 또는 vk::createInstanceUnique를 통해 인스턴스 생성
  • RAII를 사용하면 소멸 시 자동 정리
  • Validation Layer와 Extension을 std::vector<std::string>으로 관리한 뒤 .data()로 넘기거나 std::vector<const char*>를 만들어 넘기면 깔끔하게 처리 가능

예외 처리 모드 활용

Vulkan-HPP는 기본적으로 예외를 던지지 않는 모드로 동작하지만, 컴파일 옵션을 통해 예외 모드를 활성화할 수 있습니다. 예외 모드에서는 vk::createInstanceUnique 호출이 실패하면 vk::SystemError 예외를 던져 에러 처리를 단순화합니다. 예외를 사용하지 않고 싶다면 auto [result, instance] = vk::createInstance(...) 형태로 결과를 명시적으로 체크할 수도 있습니다.

여기서는 예외 모드를 사용한다고 가정합니다. (VULKAN_HPP_NO_EXCEPTIONS를 정의하지 않으면 예외 모드 활성화)

코드 예제

디렉토리 구조

my_vulkan_hpp_instance/
 ├─ CMakeLists.txt
 ├─ src/
 │   └─ main.cpp
 └─ build/

main.cpp 코드

#include <iostream>
#include <vector>
#include <vulkan/vulkan.hpp>

int main() {
    // 앱 정보
    vk::ApplicationInfo appInfo(
        "MyVulkanApp", 
        1, 
        "MyEngine", 
        1, 
        VK_API_VERSION_1_3
    );

    // Validation Layer와 Extension 설정
    // 예: Validation Layer 활성화 시 "VK_LAYER_KHRONOS_validation" 사용
    std::vector<const char*> layers = { "VK_LAYER_KHRONOS_validation" };

    // Extension은 보통 VK_EXT_debug_utils 등을 추가할 수 있음
    // 여기서는 예시로 기본 Extension만 (실제 시스템에 맞게 수정 필요)
    std::vector<const char*> extensions = { 
        "VK_EXT_debug_utils" 
    };

    vk::InstanceCreateInfo createInfo(
        {},                 // flags
        &appInfo,           // pApplicationInfo
        (uint32_t)layers.size(), layers.data(),
        (uint32_t)extensions.size(), extensions.data()
    );

    try {
        // RAII 기반 인스턴스 생성
        vk::UniqueInstance instance = vk::createInstanceUnique(createInfo);

        // 인스턴스 생성 성공
        std::cout << "Vulkan Instance created successfully with Vulkan-HPP!\n";

        // instance가 스코프를 벗어나면 자동으로 vkDestroyInstance 호출
    } catch (const vk::SystemError& err) {
        std::cerr << "Failed to create Vulkan Instance: " << err.what() << "\n";
        return 1;
    }

    return 0;
}

CMakeLists.txt 예제

cmake_minimum_required(VERSION 3.10)
project(vulkan_hpp_instance)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Vulkan REQUIRED)

add_executable(vulkan_hpp_instance src/main.cpp)
target_include_directories(vulkan_hpp_instance PRIVATE ${Vulkan_INCLUDE_DIRS})
target_link_libraries(vulkan_hpp_instance Vulkan::Vulkan)

빌드 및 실행

mkdir build
cd build
cmake ..
make
./vulkan_hpp_instance

정상적으로 인스턴스가 생성되면 “Vulkan Instance created successfully with Vulkan-HPP!”가 출력됩니다. 예외를 던질 상황(예: 잘못된 Layer나 Extension 이름을 주면) vk::SystemError 예외를 catch 블록에서 처리할 수 있습니다.

주요 포인트

  • RAII를 통한 인스턴스 자동 정리: vk::UniqueInstance
  • vector, string 등 STL 컨테이너를 활용해 Layer, Extension 관리 간소화
  • 예외 처리 모드로 에러 처리 단순화 (try/catch)
  • 코드가 C 스타일 대비 훨씬 간결하고 안전해짐

정리 및 다음 글 예고

이번 글에서는 인스턴스 생성 과정을 Vulkan-HPP 기반 Modern C++ 스타일로 재작성했습니다. RAII, 예외 처리, STL 컨테이너 등을 활용해 코드를 단순화하고 안전성을 높일 수 있음을 확인했습니다.

다음 글(#3)에서는 물리 디바이스 열람 및 로지컬 디바이스, 큐 확보 과정을 다시 C++ 스타일로 재구현해보겠습니다. 이전에는 C API로 반복문과 if문을 통해 디바이스를 선택했지만, 이제는 Vulkan-HPP와 RAII로 한층 깔끔한 코드를 작성할 수 있을 것입니다.

반응형