Vulkan Cookbook 第一章 15 建立邏輯裝置
建立邏輯裝置
邏輯裝置是在我們的應用程式中建立的最重要的物件之一。它代表從真正的硬體啟用的所有擴充套件、特性以及佇列的抽象:
邏輯裝置允許我們執行通常在渲染應用程式中完成的所有工作,例如建立影象和緩衝區、設定管道狀態或載入著色器。 它給我們最重要的能力是記錄命令(例如分配繪製呼叫或排程計算工作)並將它們提交給佇列,由給定的硬體執行和處理它們。執行此類操作後,我們將獲取提交的操作的結果。些可以是由計算著色器計算的一組值,或由繪製呼叫生成的其他資料(不一定是影象)。 所有這些都是在邏輯裝置上執行的,所以現在我們將看看如何建立一個。
準備就緒
我們將使用自定義結構型別的變數。該型別被稱為
struct QueueInfo {
uint32_t FamilyIndex;
std::vector<float> Priorities;
};
在這種型別變數中,我們將儲存一個給定裝置的請求佇列資訊。該資料包含我們希望從中建立佇列的佇列族索引,從該佇列族請求的佇列總數以及分配給每個佇列的優先順序列表。由於優先順序的數量必須等於從給定佇列族請求的佇列數量,因此我們從給定佇列族請求的佇列數量等於優先順序向量中的元素數量。
怎麼做...
1.根據功能,限制可用擴充套件和支援操作功能的型別,選擇呼叫vkEnumeratePhysicalDevices()
這個怎麼運作...
要建立邏輯裝置,我們需要準備大量資料。 首先,我們需要獲取給定物理裝置支援的擴充套件列表,然後我們需要檢查我們要啟用的所有擴充套件是否都可以在支援的擴充套件列表中找到。 與例項建立類似,我們無法建立不受支援擴充套件的邏輯裝置。 這樣的操作將失敗:
std::vector<VkExtensionProperties> available_extensions;
//譯者注:獲取物理裝置支援的擴充套件
if( !CheckAvailableDeviceExtensions( physical_device, available_extensions ) ) {
return false;
}
//譯者注:檢查物理裝置支援的擴充套件是否支援我們想要的擴充套件
for( auto & extension : desired_extensions ) {
if( !IsExtensionSupported( available_extensions, extension ) ) {
std::cout << "Extension named '" << extension << "' is not supported by a physical device." << std::endl;
return false;
}
}
接下來,我們準備一個名為queue_create_infos的向量變數,該變數將包含有關我們要為邏輯裝置請求的佇列和佇列族的資訊。 此向量的每個元素都是VkDeviceQueueCreateInfo型別。 它包含的最重要的資訊是佇列族的索引和為該系列請求的佇列數。 我們不能在向量中有兩個索引相同佇列族的元素。
在queue_create_infos向量變數中,我們還提供有關佇列優先順序的資訊。 給定族中的每個佇列可能具有不同的優先順序:浮點值介於0.0f和1.0f之間,值越高表示優先順序越高。 這意味著硬體將嘗試根據此優先順序來排程多個佇列上執行的操作,並可能為具有更高優先順序的佇列分配更多處理時間。 但是,這只是一個提示,並不能保證。 它也不會影響其他裝置的佇列:
std::vector<VkDeviceQueueCreateInfo> queue_create_infos;
for( auto & info : queue_infos ) {
queue_create_infos.push_back( { //譯者注
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, //sType
nullptr, //pNext
0, //flags
info.FamilyIndex, //queueFamilyIndex
static_cast<uint32_t>(info.Priorities.size()), //queueCount
info.Priorities.size() > 0 ? &info.Priorities[0] : nullptr //pQueuePriorities
} );
};
queue_create_infos向量變數將提供給另一個VkDeviceCreateInfo型別的變數。 在此變數中,我們儲存有關我們請求邏輯裝置佇列的不同佇列族的數量,啟用的層的數量和名稱,以及我們要為裝置啟用的擴充套件,和我們想要使用的功能(Features)的資訊。
裝置無需圖層和擴充套件即可正常工作,但有一些非常有用的擴充套件,如果我們想要在螢幕上顯示Vulkan生成的影象,則必須啟用這些擴充套件。
功能(Features)也沒有必要,因為核心Vulkan API為我們提供了大量功能,可以生成漂亮的影象或執行復雜的計算。如果我們不想啟用任何功能,我們可以為pEnabledFeatures成員提供nullptr值,或者提供一個填充零的變數。但是,如果我們想要使用更高階的功能,例如幾何或曲面細分著色器,我們需要通過提供指向適當變數的指標來啟用它們,先前獲取支援的功能列表,並確保我們需要的功能可用。可以(甚至應該)禁用不必要的功能,因為有些功能可能會影響效能。這種情況非常罕見,但記住這一點是件好事。在Vulkan中,我們應該只使用那些需要完成和使用的東西:
VkDeviceCreateInfo device_create_info = { //譯者注
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, //sType
nullptr, //pNext
0, //flags
static_cast<uint32_t>(queue_create_infos.size()), //queueCreateInfoCount
queue_create_infos.size() > 0 ? &queue_create_infos[0] : nullptr, //pQueueCreateInfos
0, //enabledLayerCount
nullptr, //ppEnabledLayerNames
static_cast<uint32_t>(desired_extensions.size()), //enabledExtensionCount
desired_extensions.size() > 0 ? &desired_extensions[0] : nullptr, //ppEnabledExtensionNames
desired_features //pEnabledFeatures
};
device_create_info變數提供給vkCreateDevice()函式,該函式建立邏輯裝置。 為了確保操作成功,我們需要檢查vkCreateDevice()函式呼叫返回的值是否等於VK_SUCCESS。 如果是,則建立的邏輯裝置的控制代碼儲存在函式呼叫的最後一個引數指向的變數中:
VkResult result = vkCreateDevice( physical_device, &device_create_info, nullptr, &logical_device );
if( (result != VK_SUCCESS) || (logical_device == VK_NULL_HANDLE) ) {
std::cout << "Could not create logical device." << std::endl;
return false;
}
return true;