😎

【Vulkan】Validation layerの設定

に公開

開発環境

エディタ: Visual Studio Code
OS: Windows11

インスタンス作成時に拡張機能とレイヤー機能を追加

validation layerを有効にするには、インスタンス作成時に、拡張機能とレイヤー機能を追加する必要があります。
拡張機能-> VK_EXT_DEBUG_UTILS_EXTENSION_NAME
レイヤー機能-> "VK_LAYER_KHRONOS_validation"

    const char *extensions[] = {VK_KHR_SURFACE_EXTENSION_NAME, "VK_KHR_win32_surface", VK_EXT_DEBUG_UTILS_EXTENSION_NAME};
    const char *layers[] = {"VK_LAYER_KHRONOS_validation"};

    VkInstanceCreateInfo createInfo{};
    createInfo.ppEnabledExtensionNames = extensions;
    createInfo.enabledExtensionCount = _countof(extensions);
    createInfo.ppEnabledLayerNames = layers;
    createInfo.enabledLayerCount = _countof(layers);
void VulkanContext::CreateInstance()
{
    VkApplicationInfo appInfo{};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pNext = nullptr;
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pApplicationName = "App Name";
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pEngineName = "No Engine";
    appInfo.apiVersion = VK_API_VERSION_1_4;

    const char *extensions[] = {VK_KHR_SURFACE_EXTENSION_NAME, "VK_KHR_win32_surface", VK_EXT_DEBUG_UTILS_EXTENSION_NAME};
    const char *layers[] = {"VK_LAYER_KHRONOS_validation"};

    VkInstanceCreateInfo createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    createInfo.pApplicationInfo = &appInfo;
    createInfo.ppEnabledExtensionNames = extensions;
    createInfo.enabledExtensionCount = _countof(extensions);
    createInfo.ppEnabledLayerNames = layers;
    createInfo.enabledLayerCount = _countof(layers);

    if (vkCreateInstance(&createInfo, nullptr, &m_vkInstance) != VK_SUCCESS)
    {
        std::cout << "Failed to create instance" << std::endl;
        std::exit(1);
    }

    std::cout << "Successfully create instance" << std::endl;
}

DebugMessengerの作成

次は、どんなメッセージを受け取るのかを設定します。

void VulkanContext::CreateDebugMessenger()
{
    VkDebugUtilsMessengerCreateInfoEXT createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
    createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
    createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
    createInfo.pfnUserCallback = vulkan_callback;

    auto vkCreateDebugUtilsMessenger = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(m_vkInstance, "vkCreateDebugUtilsMessengerEXT");

    if (vkCreateDebugUtilsMessenger && vkCreateDebugUtilsMessenger(m_vkInstance, &createInfo, nullptr, &m_debugMessenger) != VK_SUCCESS)
    {
        std::cout << "Failed to create debug meesenger" << std::endl;
        std::exit(1);
    }

    m_pfnSetDebugUtilsObjectNameEXT = (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(m_vkInstance, "vkSetDebugUtilsObjectNameEXT");

    std::cout << "Successfully create debug meesenger" << std::endl;
}

VkDebugUtilsMessengerCreateInfoEXT構造体の初期化

VkDebugUtilsMessengerCreateInfoEXT createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
createInfo.pfnUserCallback = vulkan_callback;

・Vulkanのデバッグメッセンジャーの設定情報です。
sTypeは構造体の種類を示す識別子。
messageSeverityは「どのレベルのメッセージを受け取るか」を指定。エラー、警告、詳細情報すべて受信。
messageTypeは「どの種類のメッセージを受け取るか」で、一般的なメッセージ、パフォーマンス関連、バリデーション関連すべて指定。
pfnUserCallbackはメッセージを受信したときに呼ばれるコールバック関数のポインタ。

拡張関数のポインタを取得

auto vkCreateDebugUtilsMessenger = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(m_vkInstance, "vkCreateDebugUtilsMessengerEXT");

vkCreateDebugUtilsMessengerEXTはVulkanの拡張機能に属するため、直接リンクされず動的に関数ポインタを取得します。
vkGetInstanceProcAddrでこの関数のアドレスを取得し、キャストして関数ポインタに変換しています。

デバッグメッセンジャーの作成

if (vkCreateDebugUtilsMessenger && vkCreateDebugUtilsMessenger(m_vkInstance, &createInfo, nullptr, &m_debugMessenger) != VK_SUCCESS)
{
    std::cout << "Failed to create debug meesenger" << std::endl;
    std::exit(1);
}

・取得した関数ポインタが有効なら呼び出し、デバッグメッセンジャーを作成。
・作成に失敗したらエラーメッセージを出してプログラムを終了。

オブジェクト名設定関数の取得

m_pfnSetDebugUtilsObjectNameEXT = (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(m_vkInstance, "vkSetDebugUtilsObjectNameEXT");

・オブジェクトにデバッグ用の名前を付けられる関数のポインタも取得しています。
・これは便利なデバッグ支援機能として使います。

コールバック関数

VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::vulkan_callback(VkDebugUtilsMessageSeverityFlagBitsEXT serverity, VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData)
{
    std::stringstream ss;
    ss << "[Validation Layers] " << pCallbackData->pMessage << std::endl;

#ifdef WIN32
    OutputDebugStringA(ss.str().c_str());
#else
    std::cerr << ss.str() << std::endl;
#endif

    return VK_FALSE;
}

オブジェクトネームの変更処理

void VulkanContext::SetDebugObjectName(void *objectHandle, VkObjectType type, const char *name)
{
#if _DEBUG | DEBUG
    if (m_pfnSetDebugUtilsObjectNameEXT)
    {
        VkDebugUtilsObjectNameInfoEXT nameInfo{};
        nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
        nameInfo.objectHandle = reinterpret_cast<uint64_t>(objectHandle);
        nameInfo.objectType = type;
        nameInfo.pObjectName = name;

        m_pfnSetDebugUtilsObjectNameEXT(m_vkDevice, &nameInfo);
    }
#endif
}

#if _DEBUG | DEBUG
デバッグビルド時のみ動作するように条件コンパイルしています。本番ビルドでは無効化されます。
m_pfnSetDebugUtilsObjectNameEXT
Vulkanの関数ポインタで、この拡張関数を動的に取得して利用しています。nullチェックして存在するか確認。
VkDebugUtilsObjectNameInfoEXT nameInfo{}
デバッグ用の名前をVulkanオブジェクトに付けるための情報構造体。初期化も行います。
nameInfo.sType
構造体の種類を識別する識別子。
nameInfo.objectHandle
名前を付けたいVulkanオブジェクトのハンドル。void*からuint64_tにキャストしています。
nameInfo.objectType
どの種類のVulkanオブジェクトか(例:バッファ、イメージ、セマフォなど)。
nameInfo.pObjectName
付けたい任意のC文字列の名前。
m_pfnSetDebugUtilsObjectNameEXT(m_vkDevice, &nameInfo);
実際にVulkanに名前を設定している関数の呼び出し。

Discussion