vulakn教程--Drawing a Triangle--Set up--Validation layers

发表于2016-12-14
评论0 600浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏程序行业精英群

711501594

原文链接:Vulkan-tutorial


Validation layers 验证层

为什么要用Validation layers ? 
Vulkan的设计理念是:使驱动(driver)的负担最小化。一个明显的表现就是它有限的错误检测,像设置错误的枚举值或者将必须的函数参数传递为空指针(NULL)这类简单的操作都没有明确的处理,Vulkan只是简单的Crash或者产生一些未定义的行为(undefined behavior)。因为Vulkan要求你必须知道你正在做什么。但这并不意味着我们就无从下手了,相反,Vulakn引入了一个非常优雅的方法:Validation layers.Validation layers是一个可选的组件(optional components) ,它连接Vulkan的方法调用。Validation layers 的基本功能有以下几个:

  1. 检查参数是否被勿用。
  2. 追踪对象的创建与销毁,检测是否与有资源泄露。
  3. 追踪线程(thread)调用的源头,检测线程是否安全。
  4. 将方法调用的参数打印到标准输出。
  5. Tracing Vulkan calls for profiling and replaying。

vulkan.h 中有定义:

1
#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME  "VK_EXT_debug_report"  //这是验证所需支持的扩展(extension)。

GLFW提供了一个获取extension的简便方法:

1
const char** glfwGetRequiredInstanceExtensions(uint32_t* count);

获取extension列表,并添加 VK_EXT_DEBUG_REPORT_EXTENSION_NAME (我觉得extension 列表里应该已有此扩展):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
std::vector<const char*=""> getRequiredExtensions() {
    std::vector<const char*=""> extensions;
    unsigned int glfwExtensionCount = 0;
    const char** glfwExtensions;
    glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
 
    for (unsigned int i = 0; i < glfwExtensionCount; i++) {
        extensions.push_back(glfwExtensions[i]);
    }
 
    if (enableValidationLayers) {
        extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
    }
 
    return extensions;
}

然后修改createInfo,为它添加extensions:

1
2
3
auto extensions = getRequiredExtensions();
createInfo.enabledExtensionCount = extensions.size();
createInfo.ppEnabledExtensionNames=extensions.data();

LunarG validation layers 只有在安装了LunarG SDK的PC上才能使用,这里我们不需要所有的layers,LunarG SDK 只需要VK_LAYER_LUNARG_standard_validation 即可。我们首先获取所有的layers , 然后检查是否支持VK_LAYER_LUNARG_standard_validation 。

1
VkResult vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties);

vkEnumerateInstanceLayerProperties()可以获取所有支持的VkLayerProperties,当pPropertiesnullptr时,pPropertyCount将被赋予所被支持的VkLayerProperties数量;pProperties不为nullptr时,pPropertyCount将被赋予所被支持的VkLayerProperties数量的同时,pProperties指向pPropertyCountVkLayerProperties的数组。

1
2
3
4
//我们需要的layer
const std::vector<const char*=""> validationLayers = {
    "VK_LAYER_LUNARG_standard_validation"
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//检查是否支持
bool checkValidationLayerSupport() {
    uint32_t layerCount;
    vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
    std::vector availableLayers(layerCount);
    vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
    for (const char* layerName : validationLayers) {
        bool layerFound = false;
        for (const auto& layerProperties : availableLayers) {
            if (strcmp(layerName, layerProperties.layerName) == 0) {
                layerFound = true;
                break;
            }
        }
        if (!layerFound) {
            return false;
        }
    }
    return true;
}

然后修改createInfo, 为它添加layers:

1
2
3
4
5
6
7
8
9
10
bool enableValidationLayers = checkValidationLayerSupport();
if (enableValidationLayers) {
    createInfo.enabledLayerCount = validationLayers.size();
    createInfo.ppEnabledLayerNames = validationLayers.data();
}else{
    createInfo.enabledLayerCount = 0;
}
 
//创建具有DEBUG的Instance
kResult result = vkCreateInstance(&inst_info,nullptr,&instance);

做了这些以后我们就可验证错误了吗?别急,我们还需要定义一个回调函数(callback),将debug信息带回到我们的程序中,然后显示出来。现在,让我们看看这个回调函数长什么样。定义一个具有PFN_vkDebugReportCallbackEXT原型(prototype)的静态(static)成员函数debugCallbak:

1
2
3
4
5
6
7
8
//注意这里只是windows下的实现。
#ifndef WIN32
#define __stdcall
#endif
static VkBool32 __stdcall debugCallback( VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) {
    std::cerr << "validation layer: " << msg << std::endl;
    return VK_FALSE;
}

第一个参数VkDebugReportFlagsEXT可以取值如下(可以组合使用):

1
2
3
4
5
VK_DEBUG_REPORT_INFORMATION_BIT_EXT
VK_DEBUG_REPORT_WARNING_BIT_EXT     
VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT  
VK_DEBUG_REPORT_ERROR_BIT_EXT
VK_DEBUG_REPORT_DEBUG_BIT_EXT

Msg 参数就是我们需要的debug信息,userData是我们要传给debugCallback的数据。

1
VkDebugReportCallbackEXT callback; //声明

和创建VkInstance一样,需要填充一个VkDebugReportCallbackCreateInfoEXT结构体:

1
2
3
4
5
6
7
typedef struct VkDebugReportCallbackCreateInfoEXT {
    VkStructureType sType;
    const void* pNext;
    VkDebugReportFlagsEXT flags;
    PFN_vkDebugReportCallbackEXT pfnCallback;
    void* pUserData;
} VkDebugReportCallbackCreateInfoEXT;

填充:

1
2
3
VkDebugReportCallbackCreateInfoEXT createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
createInfo.pfnCallback = debugCallback;//pUserData 也可以指定数据,类似userData

接下来会和VkInstance类似,调用 vkCreateDebugReportCallbackEXT(…)函数来创建我们的callback吗?并不尽然,因为vkCreateDebugReportCallbackEXT(…)是扩展函数,它并不会随vulkan 核心API一同加载,所以需要使用: 
PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance instance, const char* pName); 函数来加载vkCreateDebugReportCallbackEXT(...)的地址:

1
2
3
4
5
6
7
8
VkResult CreateDebugReportCallbackEXT(VkInstance instance, const    VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) {
    auto func = (PFN_vkCreateDebugReportCallbackEXT)    vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
    if (func != nullptr) {
        return func(instance, pCreateInfo, pAllocator, pCallback);
    } else {
        return VK_ERROR_EXTENSION_NOT_PRESENT;
    }
}

创建 callback:

1
2
3
if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) {
    throw std::runtime_error("failed to set up debug callback!");
}

好了,如果你运行程序,会出现两个窗体,一个是用来显示我们的三角形的,现在它是空的,另一个是命令提示窗,它显示debug信息。现在关闭窗口,会发现: 
 
糟糕,有错误!怎么可能? 它提示callbacks没有被清除。 VkDebugReportCallbackEXT 对象需要被vkDestroyDebugReportCallbackEXT清除,现在用VDeleter包裹callbacks,并添加vkDestroyDebugReportCallbackEXT函数:

1
VDeleter callback{instance, DestroyDebugReportCallbackEXT};

1
2
3
4
5
6
void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) {
    auto func = (PFN_vkDestroyDebugReportCallbackEXT)   vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
    if (func != nullptr) {
        func(instance, callback, pAllocator);
    }
}

Vulkan SDK 的目录下有个Config文件夹,这个文件下有个vk_layer_settings.txt文件,是你对Validation layers进行配置的文件,你可以对这个问文件进行配置。但在后续的教程里,我都认为你采用的是默认配置。

源码 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#define GLFW_INCLUDE_VULKAN
#include
 
#include
#include
#include
#include
#include
 
const int WIDTH = 800;
const int HEIGHT = 600;
 
const std::vector<const char*=""> validationLayers = {
    "VK_LAYER_LUNARG_standard_validation"
};
 
#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif
 
VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) {
    auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
    if (func != nullptr) {
        return func(instance, pCreateInfo, pAllocator, pCallback);
    } else {
        return VK_ERROR_EXTENSION_NOT_PRESENT;
    }
}
 
void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) {
    auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
    if (func != nullptr) {
        func(instance, callback, pAllocator);
    }
}
 
template "">
class VDeleter {
    public:
        VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {}
 
        VDeleter(std::function<void(t, vkallocationcallbacks*)=""> deletef) {
            this->deleter = [=](T obj) {
                deletef(obj, nullptr);
            };
        }
 
        VDeleter(const VDeleter& instance, std::function<void(vkinstance, t,="" vkallocationcallbacks*)=""> deletef) {
            this->deleter = [&instance, deletef](T obj) {
                deletef(instance, obj, nullptr);
            };
        }
 
        VDeleter(const VDeleter& device, std::function<void(vkdevice, t,="" vkallocationcallbacks*)=""> deletef) {
            this->deleter = [&device, deletef](T obj) {
                deletef(device, obj, nullptr);
            };
        }
 
        ~VDeleter() {
            cleanup();
        }
 
        T* operator &() {
            cleanup();
            return &object;
        }
 
        operator T() const {
            return object;
        }
 
    private:
        T object {VK_NULL_HANDLE};
        std::function<void(t)> deleter;
 
        void cleanup() {
            if (object != VK_NULL_HANDLE) {
                deleter(object);
            }
            object = VK_NULL_HANDLE;
        }
};
 
class HelloTriangleApplication {
    public:
        void run() {
            initWindow();
            initVulkan();
            mainLoop();
        }
 
    private:
        GLFWwindow* window;
 
        VDeleter instance {vkDestroyInstance};
        VDeleter callback {instance, DestroyDebugReportCallbackEXT};
 
        void initWindow() {
            glfwInit();
 
            glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
            glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
 
            window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
        }
 
        void initVulkan() {
            createInstance();
            setupDebugCallback();
        }
 
        void mainLoop() {
            while (!glfwWindowShouldClose(window)) {
                glfwPollEvents();
            }
        }
 
        void createInstance() {
            if (enableValidationLayers && !checkValidationLayerSupport()) {
                throw std::runtime_error("validation layers requested, but not available!");
            }
 
            VkApplicationInfo appInfo = {};
            appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
            appInfo.pApplicationName = "Hello Triangle";
            appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
            appInfo.pEngineName = "No Engine";
            appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
            appInfo.apiVersion = VK_API_VERSION_1_0;
 
            VkInstanceCreateInfo createInfo = {};
            createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
            createInfo.pApplicationInfo = &appInfo;
 
            auto extensions = getRequiredExtensions();
            createInfo.enabledExtensionCount = extensions.size();
            createInfo.ppEnabledExtensionNames = extensions.data();
 
            if (enableValidationLayers) {
                createInfo.enabledLayerCount = validationLayers.size();
                createInfo.ppEnabledLayerNames = validationLayers.data();
            } else {
                createInfo.enabledLayerCount = 0;
            }
 
            if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
                throw std::runtime_error("failed to create instance!");
            }
        }
 
        void setupDebugCallback() {
            if (!enableValidationLayers) return;
 
            VkDebugReportCallbackCreateInfoEXT createInfo = {};
            createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
            createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
            createInfo.pfnCallback = debugCallback;
 
            if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) {
                throw std::runtime_error("failed to set up debug callback!");
            }
        }
 
        std::vector<const char*=""> getRequiredExtensions() {
            std::vector<const char*=""> extensions;
 
            unsigned int glfwExtensionCount = 0;
            const char** glfwExtensions;
            glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
 
            for (unsigned int i = 0; i < glfwExtensionCount; i++) {
                extensions.push_back(glfwExtensions[i]);
            }
 
            if (enableValidationLayers) {
                extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
            }
 
            return extensions;
        }
 
        bool checkValidationLayerSupport() {
            uint32_t layerCount;
            vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
 
            std::vector availableLayers(layerCount);
            vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
 
            for (const char* layerName : validationLayers) {
                bool layerFound = false;
 
                for (const auto& layerProperties : availableLayers) {
                    if (strcmp(layerName, layerProperties.layerName) == 0) {
                        layerFound = true;
                        break;
                    }
                }
 
                if (!layerFound) {
                    return false;
                }
            }
 
            return true;
        }
 
        static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) {
            std::cerr << "validation layer: " << msg << std::endl;
 
            return VK_FALSE;
        }
};
 
int main() {
    HelloTriangleApplication app;
 
    try {
        app.run();
    } catch (const std::runtime_error& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }
 
    return EXIT_SUCCESS;
}

原文链接

著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

游戏学院公众号二维码
腾讯游戏学院
微信公众号

提供更专业的游戏知识学习平台