[Vulkan教程] 一: 创建VkDevice

来源:互联网 发布:汇编中结构数组的定义 编辑:程序博客网 时间:2024/06/01 08:41

这个系列的文章是写给对已有的D3D11和GL比较熟悉并理解多线程、资源暂存、同步等概念但想进一步了解它们是如何以Vulkan实现的读者。

文章并不追求通俗易懂,里面有很多术语(为了解里面的晦涩内容,建议去阅读Vulkan规范或者更深度的教程)。

为了更好地理解Vulkan的使用,文章会结合笔者正在开发的Vulkan图形库kaleido3d来做说明。

第一步:创建VkInstance


Vulkan API的初始化必须要创建实例(VkInstance)。Vulkan实例之间是相互独立的,所以你可以给不同的实例设置不同的属性(例如,是否激活validation layer和extensions)。

代码示例:创建VkInstance,为了方便调试,Debug版本加上了API校验。
#if _DEBUGconst char* DebugLevelString(VkDebugReportFlagsEXT lev){    switch (lev)    {#define STR(r) case VK_DEBUG_REPORT_ ##r ##_BIT_EXT: return #r        STR(INFORMATION);        STR(WARNING);        STR(PERFORMANCE_WARNING);        STR(ERROR);        STR(DEBUG);#undef STR    default:        return "UNKNOWN_ERROR";    }}VKAPI_ATTR VkBool32 VKAPI_CALL MyDebugReportCallback(    VkDebugReportFlagsEXT       flags,    VkDebugReportObjectTypeEXT  objectType,    uint64_t                    object,    size_t                      location,    int32_t                     messageCode,    const char*                 pLayerPrefix,    const char*                 pMessage,    void*                       pUserData){    Log::Out(LogLevel::Info, "VkDebug", "{%s}[%s]: %s ", DebugLevelString(flags), pLayerPrefix, pMessage);    return VK_FALSE;}static VkDebugReportCallbackEXT callback;#endifVkResult RHIRoot::Initializer::Init(bool enableValidation, std::string name){    VkResult err;    err = CreateInstance(enableValidation, name);    if (err)    {        Log::Out(LogLevel::Fatal, "RHIRoot::Initializer", "Could not create Vulkan instance : %s.", vkTools::errorString(err).c_str());    }    else    {        Log::Out(LogLevel::Info, "RHIRoot::Initializer", "Vulkan instance created. version %d.%d.%d", VK_VERSION_MAJOR(VK_API_VERSION), VK_VERSION_MINOR(VK_API_VERSION), VK_VERSION_PATCH(VK_API_VERSION));    }#if _DEBUG    /* Load VK_EXT_debug_report entry points in debug builds */    PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT =        reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>        (vkGetInstanceProcAddr(Instance, "vkCreateDebugReportCallbackEXT"));    PFN_vkDebugReportMessageEXT vkDebugReportMessageEXT =        reinterpret_cast<PFN_vkDebugReportMessageEXT>        (vkGetInstanceProcAddr(Instance, "vkDebugReportMessageEXT"));    PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT =        reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>        (vkGetInstanceProcAddr(Instance, "vkDestroyDebugReportCallbackEXT"));    VkDebugReportCallbackCreateInfoEXT callbackCreateInfo;    callbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;    callbackCreateInfo.pNext = nullptr;    callbackCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT |        VK_DEBUG_REPORT_WARNING_BIT_EXT |        VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;    callbackCreateInfo.pfnCallback = &MyDebugReportCallback;    callbackCreateInfo.pUserData = nullptr;    VkResult result = vkCreateDebugReportCallbackEXT(Instance, &callbackCreateInfo, nullptr, &callback);#endif    uint32_t gpuCount;    err = vkEnumeratePhysicalDevices(Instance, &gpuCount, nullptr);    std::vector<VkPhysicalDevice> deviceList(gpuCount);    err = vkEnumeratePhysicalDevices(Instance, &gpuCount, deviceList.data());    Log::Out(LogLevel::Info, "RHIRoot::Initializer", "Device Count : %d", gpuCount);    PhysicalDevices.swap(deviceList);    return err;}VkResult RHIRoot::Initializer::CreateInstance(bool enableValidation, std::string name){    VkApplicationInfo appInfo = {};    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;    appInfo.pApplicationName = name.c_str();    appInfo.pEngineName = name.c_str();    appInfo.apiVersion = VK_API_VERSION;    std::vector<const char*> enabledExtensions = { VK_KHR_SURFACE_EXTENSION_NAME };#if K3DPLATFORM_OS_WIN    enabledExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);#else    // todo : linux/android    enabledExtensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);#endif    VkInstanceCreateInfo instanceCreateInfo = {};    instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;    instanceCreateInfo.pNext = NULL;    instanceCreateInfo.pApplicationInfo = &appInfo;    if (enabledExtensions.size() > 0)    {        if (enableValidation)        {            enabledExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);        }        instanceCreateInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size();        instanceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data();    }    if (enableValidation)    {        instanceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount;        instanceCreateInfo.ppEnabledLayerNames = vkDebug::validationLayerNames;    }    return vkCreateInstance(&instanceCreateInfo, nullptr, &Instance);}void RHIRoot::Initializer::Destroy(){#if _DEBUG    PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT =        reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>        (vkGetInstanceProcAddr(Instance, "vkDestroyDebugReportCallbackEXT"));    vkDestroyDebugReportCallbackEXT(Instance, callback, nullptr);#endif    vkDestroyInstance(Instance, nullptr);    Log::Out(LogLevel::Info, "RHIRoot::Initializer", "Destroy Instance.");}

通过VkInstance可以检查GPU设备是否可用。Vulkan不一定是运行在GPU上,但我们把问题简单化。每个GPU都会提供一个句柄 - VkPhysicalDevice。你可以通过它来查询GPU厂商、属性(vkGetPhysicalDeviceProperties)、能力(vkGetPhysicalDeviceFeatures)等。

代码示例: 枚举所有可用的GPU设备
void EnumAllDeviceAdapter(rhi::IDeviceAdapter** & adapterList, uint32* count){    *count = (uint32)RHIRoot::GetPhysicDevices().size();    adapterList = new rhi::IDeviceAdapter*[*count];    for (size_t i = 0; i < *count; i++)    {        VkPhysicalDeviceProperties properties;        vkGetPhysicalDeviceProperties(RHIRoot::GetPhysicDevices()[i], &properties);        adapterList[i] = new DeviceAdapter(&(RHIRoot::GetPhysicDevices()[i]));        Log::Out(LogLevel::Info, "EnumAllDeviceAdapter", "DeviceName is %s, VendorId is %d.", properties.deviceName, properties.vendorID);    }}

第二步:创建VkDevice


通过VkPhysicalDevice可以创建VkDevice。VkDevice是主要的调用句柄,它在逻辑上与GPU相联系。它相当于GL Context或者D3D11 Device。

一个VkInstance可以有多个VkPhysicalDevice,一个VkPhysicalDevice可以有多个VkDevice。在Vulkan1.0,跨GPU的调用还未实现(将来会)。

大概的初始化调用就像是这样:vkCreateInstance()vkEnumeratePhysicalDevices()vkCreateDevice() 。对于一个简陋的HelloTriangle程序来说,你只需要简单地将第一个物理设备作为主要的VkDevice,然后打开这个设备的相应属性(错误上报、API调用检查)进行开发就行了。

代码示例:
rhi::IDevice::ResultDevice::Create(rhi::IDeviceAdapter* pAdapter, bool withDbg){    m_pPhysicDevice = static_cast<DeviceAdapter*>(pAdapter)->m_pPDevice;    VkPhysicalDevice& physicalDevice = *static_cast<DeviceAdapter*>(pAdapter)->m_pPDevice;    uint32_t graphicsQueueIndex = 0;    vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &m_QueueCount, NULL);    K3D_ASSERT(m_QueueCount >= 1);    Log::Out(LogLevel::Info, "Device", "PhysicDeviceQueue count = %d.", m_QueueCount);    std::vector<VkQueueFamilyProperties> queueProps;    queueProps.resize(m_QueueCount);    vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &m_QueueCount, queueProps.data());    for (graphicsQueueIndex = 0; graphicsQueueIndex < m_QueueCount; graphicsQueueIndex++)    {        if (queueProps[graphicsQueueIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT)        {            m_GfxQueueIndex = graphicsQueueIndex;            Log::Out(LogLevel::Info, "Device", "graphicsQueueIndex(%d) queueFlags(%d).", graphicsQueueIndex, queueProps[graphicsQueueIndex].queueFlags);            break;        }    }    K3D_ASSERT(graphicsQueueIndex < m_QueueCount);    std::array<float, 1> queuePriorities = { 0.0f };    VkDeviceQueueCreateInfo queueCreateInfo = {};    queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;    queueCreateInfo.queueFamilyIndex = graphicsQueueIndex;    queueCreateInfo.queueCount = 1;    queueCreateInfo.pQueuePriorities = queuePriorities.data();    std::vector<const char*> enabledExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };    VkDeviceCreateInfo deviceCreateInfo = {};    deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;    deviceCreateInfo.pNext = NULL;    deviceCreateInfo.queueCreateInfoCount = 1;    deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;    deviceCreateInfo.pEnabledFeatures = NULL;#if _DEBUG    deviceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount;    deviceCreateInfo.ppEnabledLayerNames = vkDebug::validationLayerNames;#endif    if (enabledExtensions.size() > 0)    {        deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size();        deviceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data();    }    VkResult err = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &m_Device);    vkGetPhysicalDeviceMemoryProperties(physicalDevice, &m_MemoryProperties);    if (err)    {        Log::Out(LogLevel::Fatal, "Device", "Create: Could not create Vulkan Device : %s.", vkTools::errorString(err).c_str());        return rhi::IDevice::DeviceNotFound;    }    else {        m_ResourceManager.swap(std::make_unique<ResourceManager>(this, 1024, 1024));        m_ContextPool.swap(std::make_unique<CommandContextPool>(this));        InitCmdQueue(VK_QUEUE_GRAPHICS_BIT, graphicsQueueIndex, 0);        return rhi::IDevice::DeviceFound;    }}

参考

  1. Vulkan in 30 minutes
  2. Using The Vulkan validation layers
  3. TsinStudio/AndroidDev Post
0 0
原创粉丝点击