• 爲繪製和顯示操作創建 VkDevice(邏輯設備) 和 VkQueue(隊列)
選擇物理設備後,我們還需要一個邏輯設備來作爲和物理設備交互的接口。
我們還需要指定使用的隊列所屬的隊列族。
對於同一個物理設備,我們可以根據需求的不同,創建多個邏輯設備。
部分示例:
VkDevice device;//邏輯設備--3
VkQueue graphicsQueue;//邏輯設備的隊列句柄,自動被清除--3
//創建一個邏輯設備--3
void createLogicalDevice(){
//獲取帶有圖形能力的隊列族
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
/**
目前而言,對於每個隊列族,驅動程序只允許創建很少數量的隊列,但實際上,
對於每一個隊列族,我們很少需要一個以上的隊列。
我們可以在多個線程創建指令緩衝,然後在主線程一次將它們全部提交,降低調用開銷。
Vulkan需要我們賦予隊列一個0.0到1.0之間的浮點數作爲優先級來控制指令緩衝的執行順序。
即使只有一個隊列,我們也要顯式地賦予隊列優先級
*/
float queuePriority = 1.0f;
//描述了針對一個隊列族我們所需的隊列數量。
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex=indices.graphicsFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
//指定應用程序使用的設備特性
VkPhysicalDeviceFeatures deviceFeatures = {};
//創建邏輯設備相關信息
VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.queueCreateInfoCount = 1;
createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.pEnabledFeatures = &deviceFeatures;
if(enableValidationLayers){
//以對設備和 Vulkan 實例使用相同地校驗層
createInfo.enabledLayerCount =
static_cast<uint32_t>(validataionLayers.size());
createInfo.ppEnabledLayerNames = validataionLayers.data();
}else{
createInfo.enabledLayerCount = 0;
}
/**
vkCreateDevice 函數的參數:
1.創建的邏輯設備進行交互的物理設備對象
2.指定的需要使用的隊列信息
3.可選的分配器回調
4.用來存儲返回的邏輯設備對象的內存地址
邏輯設備並不直接與Vulkan實例交互,所以創建邏輯設備時不需要使用Vulkan實例作爲參數
*/
//創建邏輯設備
if(vkCreateDevice(physicalDevice,&createInfo,nullptr,
&device) != VK_SUCCESS){
throw std::runtime_error("failed to create logical device!");
}
//獲取指定隊列族的隊列句柄
//它的參數依次是邏輯設備對象,隊列族索引,隊列索引,用來存儲返回的隊列句柄的內存地址
//我們只創建了一個隊列,所以,可以直接使用索引 0
vkGetDeviceQueue(device,indices.graphicsFamily,0,&graphicsQueue);
}
設備隊列和隊列系列
與其他圖形API不同,Vulkan將設備隊列暴露給程序員,以便程序員可以決定使用多少隊列以及使用哪種類型的隊列。
隊列是用於向硬件提交命令的抽象機制。稍後您將看到Vulkan應用程序如何構建一個充滿命令的命令緩衝區,然後將它們提交到隊列以供GPU硬件進行異步處理。
Vulkan根據隊列類型將隊列排列到隊列系列中。爲了找到您感興趣的隊列的類型和特徵,您可以從物理設備查詢QueueFamilyProperties:
typedef struct VkQueueFamilyProperties {
VkQueueFlags queueFlags;
uint32_t queueCount;
uint32_t timestampValidBits;
VkExtent3D minImageTransferGranularity;
} VkQueueFamilyProperties;
typedef enum VkQueueFlagBits {
VK_QUEUE_GRAPHICS_BIT = 0x00000001,
VK_QUEUE_COMPUTE_BIT = 0x00000002,
VK_QUEUE_TRANSFER_BIT = 0x00000004,
VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008,
} VkQueueFlagBits;
該VkQueueFamilyProperties結構稱爲“系列”,因爲可能有許多(queueCount)隊列可用於特定的一組queueFlags。例如,一個系列中可能有8個具有該VK_QUEUE_GRAPHICS_BIT組的隊列。
設備上的隊列和隊列系列可能如下所示:
該VkQueueFlagBits指示每個硬件隊列可以處理什麼類型的工作負載。例如,設備可以定義VK_QUEUE_GRAPHICS_BIT爲正常3D圖形工作設置的隊列族 。但是,如果同一設備具有用於執行像素塊複製(blits)的專用硬件,則它可以僅使用該VK_QUEUE_TRANSFER_BIT 集定義另一個隊列系列。這使得硬件可以與blit工作負載並行處理圖形工作負載。
一些更簡單的GPU硬件可能只通告一個具有多個隊列類型標誌集的隊列系列:
創建設備
///4.創建設備
VkDeviceQueueCreateInfo queue_info = {};
uint32_t queue_family_count=0;
vkGetPhysicalDeviceQueueFamilyProperties(gpus[0], &queue_family_count, NULL);
assert(queue_family_count >= 1);
std::vector<VkQueueFamilyProperties> queue_props(queue_family_count);
//獲取隊列信息
vkGetPhysicalDeviceQueueFamilyProperties(gpus[0], &queue_family_count,
queue_props.data());
std::cout <<queue_family_count<<" "<<queue_props.size() <<std::endl;
bool found = false;
for (unsigned int i = 0; i < queue_family_count; i++) {
//現在只對繪製簡單圖形感興趣,所以只需要查找圖形位
if (queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
//note:隊列系列不是用句柄表示,而是用索引表示
queue_info.queueFamilyIndex = i;
found = true;
break;
}
}
std::cout <<"found:"<< found << std::endl;
//如果有多個隊列可用,則更復雜的程序可能希望在多個隊列上提交圖形命令
//這裏只有一個隊列,所以放入的值queue_priorities[] 並不重要
float queue_priorities[1] = {0.0};
queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_info.pNext = NULL;
queue_info.queueCount = 1;
queue_info.pQueuePriorities = queue_priorities;
//構建設備信息
VkDeviceCreateInfo device_info = {};
device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_info.pNext = NULL;
device_info.queueCreateInfoCount = 1;
device_info.pQueueCreateInfos = &queue_info;
device_info.enabledExtensionCount = 0;
device_info.ppEnabledExtensionNames = NULL;
//最近在Vulkan中不推薦使用設備圖層,因此在創建設備時不必指定任何圖層
device_info.enabledLayerCount = 0;
device_info.ppEnabledLayerNames = NULL;
device_info.pEnabledFeatures = NULL;
VkDevice device;//創建設備
res = vkCreateDevice(gpus[0], &device_info, NULL, &device);
assert(res == VK_SUCCESS);
///
vkDestroyDevice(device, NULL);