打破 vulkan 以测试调试回调是否设置正确的好方法是什么?

What is a good way to break vulkan to test if a debug callback was setup properly?

我是第一次学习 vulkan,目前处于 vulkan tutorial 的第 2 步。绘制三角形->设置->验证层。

我设置了错误回调,现在我想测试它是否真的有效(我没有收到任何错误消息)。

我检查过我的所有函数都被调用了(唯一不打印任何内容的是 debugCallBack 本身)。

好像什么都没坏。我怎样才能打破东西来测试错误回调是否设置正确?

例如,如果这是 OpenGL,我会尝试在标准程序 (0) 中设置一个随机的统一名称。

提前致谢。

我一直在学习不同的教程;但是,我有一组文件 Validation.hValidation.cpp 用于 Vulkan's ValidationLayers 它们非常简单,因为它们只包含一个独立的函数。使用 ValidationLayers 的文件如下所示:

-Validation.h-

#pragma once

#include <vulkan\vulkan.h>

void ErrorCheck( VkResult res );

-Validation.cpp-

#include "Validation.h"

#include <assert.h>
#include <iostream>

void ErrorCheck( VkResult res ) {
    if ( res != VK_SUCCESS ) {
        std::cout << "Error" << std::endl;
        assert( 0 && "There Was An Error" );
    }
}

这只是一个简单的函数,用于将消息打印到控制台并断言存在错误。实际的 ValidationLayers 位于不同的位置,如下所示。


现在要设置 Validation Layers 我有一个名为 VulkanInstance 的 class。 class 看起来像这样:

-VulkanInstance.h-

#pragma once

#include "VulkanConfiguration.h"

#include <vector>
#include <vulkan\vulkan.h>

class VulkanInstance {
private:
    VkInstance m_instance;
    std::vector<const char*> m_layers;
    std::vector<const char*> m_extensions;

public:
    VulkanInstance( VulkanConfiguration& vulkan_config );
    ~VulkanInstance();

    VkInstance& getInstance();
};

-VulkanInstance.cpp-

#include "VulkanInstance.h"

#include "Validation.h"
#include "Initializers.h"

#include <iostream>

VulkanInstance::VulkanInstance( VulkanConfiguration& vulkan_config ) {
    m_layers.push_back("VK_LAYER_LUNARG_standard_validation");
    m_extensions.push_back("VK_EXT_debug_report");

    VkApplicationInfo application_info = Initializers::applicationInfo( vulkan_config );
    VkInstanceCreateInfo instance_info = Initializers::instanceCreateInfo( application_info, m_layers, m_extensions );
    ErrorCheck( vkCreateInstance( &instance_info, NULL, &m_instance ) );
}

VulkanInstance::~VulkanInstance() {
    vkDestroyInstance( m_instance, NULL );
}

VkInstance& VulkanInstance::getInstance() {
    return m_instance;
}

由于上面的 class 确实有几个依赖项,我将向您展示 VulkanConfigurationInitializersVulkanConfiguration 只是一个 header 而 Initializers 是一堆独立的函数,用于创建 Vulkan 类型 objects 包装在 namespace 中。我将展示完整的 Initializers header 但我只会展示 ValidationLayersVulkanInstance 所需的相关函数声明。


-VulkanConfiguration.h-

#pragma once

#include <vulkan\vulkan.h>

struct VulkanConfiguration {
    const char* application_name = "";
    uint32_t application_version = VK_MAKE_VERSION( 0, 0, 0 );
    const char* engine_name = "My Vulkan Engine";
    const uint32_t engine_version = VK_MAKE_VERSION( 0, 0, 0 );
    const uint32_t api_version = VK_MAKE_VERSION( 1, 1, 82 );
};

-Initializers.h-

#pragma once

#include <vulkan\vulkan.h>
#include <vector>

struct VulkanConfiguration;

namespace Initializers {    
    VkApplicationInfo applicationInfo( VulkanConfiguration& config );
    VkInstanceCreateInfo instanceCreateInfo( VkApplicationInfo& app_info, std::vector<const char*>& layers, std::vector<const char*>& extensions );
    VkDeviceQueueCreateInfo deviceQueueCreateInfo( uint32_t queue_family_index, float& priority );
    VkDeviceCreateInfo deviceCreateInfo( std::vector<VkDeviceQueueCreateInfo>& queue_create_infos, VkPhysicalDeviceFeatures& device_features );
    VkCommandPoolCreateInfo commandPoolCreateInfo( uint32_t queue_family_index, VkCommandPoolCreateFlags flags = 0 );
    VkCommandBufferAllocateInfo commandBufferAllocateInfo( VkCommandPool pool, uint32_t count );
    VkBufferCreateInfo bufferCreate( VkDeviceSize size, VkBufferUsageFlags usage );
    VkMemoryAllocateInfo memoryAllocateInfo( VkDeviceSize size, uint32_t memory_type_index );
}

-Intializers.cpp-

#include "Initializers.h"
#include "VulkanConfiguration.h"

VkApplicationInfo Initializers::applicationInfo( VulkanConfiguration& config ) {
    VkApplicationInfo info = {};
    info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    info.pApplicationName = config.application_name;
    info.pEngineName = config.engine_name;
    info.applicationVersion = config.application_version;
    info.engineVersion = config.engine_version;
    info.apiVersion = config.api_version;
    return info;
}

VkInstanceCreateInfo Initializers::instanceCreateInfo( VkApplicationInfo& app_info, std::vector<const char*>& layers, std::vector<const char*>& extensions ) {
    VkInstanceCreateInfo info = {};
    info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    info.pApplicationInfo = &app_info;
    info.enabledExtensionCount = extensions.size();
    info.enabledLayerCount = layers.size();
    info.ppEnabledExtensionNames = extensions.data();
    info.ppEnabledLayerNames = layers.data();
    return info;
} 

这应该为我们提供了设置 ValidationLayers 所需的一切。


现在要完全使用 Vulkan 运行ning,您确实需要 VulkanDeviceVulkanPhysicalDeviceQueueFamilyIndices,但是我敢肯定,因为您可能已经在渲染三角形你应该把它们放在适当的位置。为了演示如何让 ValidationLayers 工作,我将向您展示 VulkanDevice class...

的构造函数和析构函数

-VulkanDevice.cpp- -- 部分源码

#include "VulkanDevice.h"

#include "Initializers.h"
#include "Validation.h"
#include "VulkanPhysicalDevice.h"

#include <vector>

VulkanDevice::VulkanDevice( VulkanInstance* instance, VulkanPhysicalDevice* physical_device ) {
    m_instance = instance;
    m_vulkan_physical_device = physical_device;

    std::vector<VkDeviceQueueCreateInfo> queue_create_infos;
    float priority = 1.0f;
    queue_create_infos.push_back( Initializers::deviceQueueCreateInfo( m_vulkan_physical_device->getQueueFamilyIndices().compute_indices, priority ) );

    VkDeviceCreateInfo create_info = Initializers::deviceCreateInfo( queue_create_infos, m_vulkan_physical_device->getPhysicalDeviceFeatures() );

    ErrorCheck( vkCreateDevice(
        m_vulkan_physical_device->getPhysicalDevice(),
        &create_info,
        nullptr,
        &m_device
    ) );

    vkGetDeviceQueue(
        m_device,
        m_vulkan_physical_device->getQueueFamilyIndices().compute_indices,
        0,
        &m_compute_queue
    );

    VkCommandPoolCreateInfo compute_pool_info = Initializers::commandPoolCreateInfo( m_vulkan_physical_device->getQueueFamilyIndices().compute_indices );

    ErrorCheck( vkCreateCommandPool(
        m_device,
        &compute_pool_info,
        nullptr,
        &m_compute_command_pool
    ) );
}

VulkanDevice::~VulkanDevice() {
    vkDestroyCommandPool(
        m_device,
        m_compute_command_pool,
        nullptr
    );

    vkDestroyDevice(
        m_device,
        nullptr
    );
}

在这里您可以看到,在构造函数中,我在自由函数 ErrorCheck() 中调用了 vkCreateDevice()vkCreateCommandPool()。截至目前,我编译时的代码库 运行 它 returns 值为 0 并且没有错误。但是,在这个 class 的析构函数中;如果我为 CommandPoolDevice:

注释掉任何一个 vkDestroy... 函数
VulkanDevice::~VulkanDevice() {
    /*
    vkDestroyCommandPool(
        m_device,
        m_compute_command_pool,
        nullptr
    );
    */

    vkDestroyDevice(
        m_device,
        nullptr
    );
}

VulkanDevice::~VulkanDevice() {
    vkDestroyCommandPool(
        m_device,
        m_compute_command_pool,
        nullptr
    );

    /*
    vkDestroyDevice(
        m_device,
        nullptr
    );
    */
}

它将分别向控制台提供这些打​​印消息:

VUID-vkDestroyDevice-device-00378(ERROR / SPEC): msgNum: 614466292 - OBJ ERROR : For device 0x42af310, CommandPool objec
t 0x1 has not been destroyed. The spec valid usage text states 'All child objects created on device must have been destr
oyed prior to destroying device' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-vkDestroyDevic
e-device-00378)
    Objects: 1
       [0] 0x1, type: 25, name: (null)
Validation(ERROR): msg_code: 614466292:  [ VUID-vkDestroyDevice-device-00378 ]  [ VUID-vkDestroyDevice-device-00378 ] Ob
ject: 0x1 (Type = 25) | OBJ ERROR : For device 0x42af310, CommandPool object 0x1 has not been destroyed. The spec valid
usage text states 'All child objects created on device must have been destroyed prior to destroying device' (https://www
.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-vkDestroyDevice-device-00378)

C:\Users\...\Vulkan Tutorial.exe (process 1256) exited w
ith code 0.
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the conso
le when debugging stops.
Press any key to close this window . . .

UNASSIGNED-ObjectTracker-ObjectLeak(ERROR / SPEC): msgNum: -1 - OBJ ERROR : VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT objec
t 0x4139590 has not been destroyed.
    Objects: 1
       [0] 0x4139590, type: 3, name: (null)
Validation(ERROR): msg_code: -1:  [ UNASSIGNED-ObjectTracker-ObjectLeak ]  [ UNASSIGNED-ObjectTracker-ObjectLeak ] Objec
t: 0x4139590 (Type = 3) | OBJ ERROR : VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT object 0x4139590 has not been destroyed.

C:\Users\...\Vulkan Tutorial.exe (process 2480) exited w
ith code 0.
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the conso
le when debugging stops.
Press any key to close this window . . .

希望这能让您从一般意义上了解 Vulkan 及其 ValidationLayers 的工作原理。上面的代码不是我自己的,因为它来自 this Youtube 频道的在线教程。

我在 https://github.com/KhronosGroup/Vulkan-Loader/issues/123 中报告了这个特殊问题。

同时我做了两件事:

1) 我分别使用 vkDebugReportMessageEXTvkSubmitDebugUtilsMessageEXT 打印介绍信息。这表明调试回调有效。

2) 打破验证的简单方法是 vkEnumeratePhysicalDevices( instance, nullptr, nullptr );