vulakn教程--Drawing a Triangle--Set up--Logical Device

发表于2016-12-09
评论0 2.7k浏览

原文链接 : Vulakn-tutorial


Logical Device

只有Physical Device 还不行,我们还需要创建Logical Device 来与它相联。Logical Device的创建和VkInstance的创建过程差不多,需要明确我们所需的特性(features)、extensions、Validation layers 、queue等。

声明 :

1
VDeleter device{vkDestroyDevice};

我们不打算使例子太复杂,特性(fetures)采用默认值Vk_FALSE,当我们想做一些更有趣的事情的时候,可以再回过头来修改。

1
VkPhysicalDeviceFeatures deviceFeatures = {};

首先,我们来看一个和队列有关的,一个很重要的结构体VkDeviceQueueCreateInfo:

1
2
3
4
5
6
7
8
typedef struct VkDeviceQueueCreateInfo {
    VkStructureType             sType;
    const void*                 pNext;
    VkDeviceQueueCreateFlags    flags;
    uint32_t                    queueFamilyIndex;
    uint32_t                    queueCount;
    const float*                pQueuePriorities;
} VkDeviceQueueCreateInfo;


说明 : flags 保留未来使用(reserved for future use),后面3个参数表示,创建queueCount个queueFamilyIndex类型的队列,每个队列的优先级用pQueuePriorities数组表示。优先级的值为0.0~1.0 , 值越大优先级越高。

填充 :

1
2
3
4
5
6
7
8
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = indices.graphicsFamily;
queueCreateInfo.queueCount = 1; //创建一个队列
 
float queuePriority = 1.0f;
queueCreateInfo.pQueuePriorities = &queuePriority;

像创建其他Vulkan对象一样,必不可少的是Vk_XXX_CreateInfo结构体,这次我们需要 VkDeviceCreateInfo:

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct VkDeviceCreateInfo {
    VkStructureType                    sType;
    const void*                        pNext;
    VkDeviceCreateFlags                flags;//(future use)
    uint32_t                           queueCreateInfoCount;
    const VkDeviceQueueCreateInfo*     pQueueCreateInfos;
    uint32_t                           enabledLayerCount;
    const char* const*                 ppEnabledLayerNames;
    uint32_t                           enabledExtensionCount;
    const char* const*                 ppEnabledExtensionNames;
    const VkPhysicalDeviceFeatures*    pEnabledFeatures;
} VkDeviceCreateInfo;

说明: 该结构除了对队列(queue)和特性(features)支持的限定外,还有对Validation layers 和 Extensions的限定,例如一个很重要的extension : VK_KHR_swapchain 支持,同样,我们不想把问题复杂化,正如在创建VkInstance时定义的那样,我们直接将那时定义的layers 和 extensions应用到这里,所不同的是现在是创建VkDevice阶段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.queueCreateInfoCount = 1;
createInfo.pEnabledFeatures = &deviceFeatures;
 
createInfo.enabledExtensionCount = 0;  //暂时不使用扩展
if (enableValidationLayers) {
    createInfo.enabledLayerCount = validationLayers.size();
    createInfo.ppEnabledLayerNames = validationLayers.data();
} else {
    createInfo.enabledLayerCount = 0;
}
//创建logical device
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
    throw std::runtime_error("failed to create logical device!");
}

这里enableValidationLayers和validationLayers直接取自创建VkInstances时已有的定义。 
我们在VkDeviceCreateInfo 里定义的队列(queue 类型为VkQueue)将会随着logical device 一同被创建。那么我们怎么获得这个队列的句柄(handle)呢 ?

1
2
3
4
VkQueue graphicsQueue;
vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue);
1
2

参数说明 : 
device : logical device. 
indices.graphicsFamily : 队列种类。 
queueIndex : 这里是 0 ,因为我们只创建了一个队列,所以这里索引为0. 
VkQueue * : &graphicsQueue


源码:

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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
#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;
    }
};
 
struct QueueFamilyIndices {
    int graphicsFamily = -1;
 
    bool isComplete() {
        return graphicsFamily >= 0;
    }
};
 
class HelloTriangleApplication {
public:
    void run() {
        initWindow();
        initVulkan();
        mainLoop();
    }
 
private:
    GLFWwindow* window;
 
    VDeleter instance{vkDestroyInstance};
    VDeleter callback{instance, DestroyDebugReportCallbackEXT};
    VDeleter surface{instance, vkDestroySurfaceKHR};
 
    VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
    VDeleter device{vkDestroyDevice};
 
    VkQueue graphicsQueue;
 
    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();
        pickPhysicalDevice();
        createLogicalDevice();
    }
 
    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!");
        }
    }
 
    void pickPhysicalDevice() {
        uint32_t deviceCount = 0;
        vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
 
        if (deviceCount == 0) {
            throw std::runtime_error("failed to find GPUs with Vulkan support!");
        }
 
        std::vector devices(deviceCount);
        vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
 
        for (const auto& device : devices) {
            if (isDeviceSuitable(device)) {
                physicalDevice = device;
                break;
            }
        }
 
        if (physicalDevice == VK_NULL_HANDLE) {
            throw std::runtime_error("failed to find a suitable GPU!");
        }
    }
 
    void createLogicalDevice() {
        QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
 
        VkDeviceQueueCreateInfo queueCreateInfo = {};
        queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
        queueCreateInfo.queueFamilyIndex = indices.graphicsFamily;
        queueCreateInfo.queueCount = 1;
 
        float queuePriority = 1.0f;
        queueCreateInfo.pQueuePriorities = &queuePriority;
 
        VkPhysicalDeviceFeatures deviceFeatures = {};
 
        VkDeviceCreateInfo createInfo = {};
        createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
 
        createInfo.pQueueCreateInfos = &queueCreateInfo;
        createInfo.queueCreateInfoCount = 1;
 
        createInfo.pEnabledFeatures = &deviceFeatures;
 
        createInfo.enabledExtensionCount = 0;
 
        if (enableValidationLayers) {
            createInfo.enabledLayerCount = validationLayers.size();
            createInfo.ppEnabledLayerNames = validationLayers.data();
        } else {
            createInfo.enabledLayerCount = 0;
        }
 
        if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
            throw std::runtime_error("failed to create logical device!");
        }
 
        vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue);
    }
 
    bool isDeviceSuitable(VkPhysicalDevice device) {
        QueueFamilyIndices indices = findQueueFamilies(device);
 
        return indices.isComplete();
    }
 
    QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
        QueueFamilyIndices indices;
 
        uint32_t queueFamilyCount = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
 
        std::vector queueFamilies(queueFamilyCount);
        vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
 
        int i = 0;
        for (const auto& queueFamily : queueFamilies) {
            if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                indices.graphicsFamily = i;
            }
 
            if (indices.isComplete()) {
                break;
            }
 
            i++;
        }
 
        return indices;
    }
 
    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;
}const>const>void(t)>void(vkdevice,>void(vkinstance,>void(t,>const>

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