您的位置:首页 > 其它

[Vulkan教程] 一: 创建VkDevice

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

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

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

第一步:创建VkInstance

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

代码示例:创建VkInstance,为了方便调试,Debug版本加上了API校验。


#if _DEBUG
const 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;
#endif

VkResult 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::Result
Device::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;
}
}


参考

Vulkan in 30 minutes

Using The Vulkan validation layers

TsinStudio/AndroidDev Post
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  vulkan