vulakn教程--Drawing a Triangle--Set up--Logical Device
原文链接 : Vulakn-tutorial
Logical Device
只有Physical Device 还不行,我们还需要创建Logical Device 来与它相联。Logical Device的创建和VkInstance
的创建过程差不多,需要明确我们所需的特性(features)、extensions、Validation layers 、queue等。
声明 :
1 | VDeleter |
我们不打算使例子太复杂,特性(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 , 值越大优先级越高。
填充 :
12345678 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
:
123456789101112 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阶段。
1234567891011121314151617 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)呢 ?
1234 VkQueue graphicsQueue;
vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue);
1
2
参数说明 : device
: logical device. indices.graphicsFamily
: 队列种类。 queueIndex
: 这里是 0 ,因为我们只创建了一个队列,所以这里索引为0. VkQueue *
: &graphicsQueue
。
源码:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 #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
>