1. 程式人生 > >Vulkan Cookbook 第一章 15 建立邏輯裝置

Vulkan Cookbook 第一章 15 建立邏輯裝置

建立邏輯裝置

邏輯裝置是在我們的應用程式中建立的最重要的物件之一。它代表從真正的硬體啟用的所有擴充套件、特性以及佇列的抽象:

邏輯裝置允許我們執行通常在渲染應用程式中完成的所有工作,例如建立影象和緩衝區、設定管道狀態或載入著色器。 它給我們最重要的能力是記錄命令(例如分配繪製呼叫或排程計算工作)並將它們提交給佇列,由給定的硬體執行和處理它們。執行此類操作後,我們將獲取提交的操作的結果。些可以是由計算著色器計算的一組值,或由繪製呼叫生成的其他資料(不一定是影象)。 所有這些都是在邏輯裝置上執行的,所以現在我們將看看如何建立一個。

準備就緒

我們將使用自定義結構型別的變數。該型別被稱為

QueueInfo,定義如下:

struct QueueInfo {
  uint32_t FamilyIndex; 
  std::vector<float> Priorities;
};

在這種型別變數中,我們將儲存一個給定裝置的請求佇列資訊。該資料包含我們希望從中建立佇列的佇列族索引,從該佇列族請求的佇列總數以及分配給每個佇列的優先順序列表。由於優先順序的數量必須等於從給定佇列族請求的佇列數量,因此我們從給定佇列族請求的佇列數量等於優先順序向量中的元素數量。

怎麼做...

1.根據功能,限制可用擴充套件和支援操作功能的型別,選擇呼叫vkEnumeratePhysicalDevices()

函式獲取的其中一個物理裝置(請參閱列舉可用的物理裝置)。獲取其控制代碼並將其儲存在名為physical_deviceVkPhysicalDevice型別變數中。 2.準備要啟用的裝置擴充套件的列表。在名為desired_extensionsstd::vector<char const *>型別變數中儲存所需擴充套件的名稱。 3.建立名為available_extensionsstd::vector<VkExtensionProperties>型別變數。獲取所有可用擴充套件的列表,並將其儲存在available_extensions變數中(參見檢查可用的裝置擴充套件
)。 4.確保來自desired_extension變數的每個擴充套件的的名稱也出現在available_extensions中。 5.準備名為desired_featuresVkPhysicalDeviceFeatures型別變數。 6.獲取由physical_device控制代碼表示的物理裝置支援的一組功能(Features)將其儲存在desired_features變數中(請參閱獲取物理裝置的功能和屬性)。 7.確保physical_device變量表示的給定物理裝置支援所有必需的功能。 通過檢查所獲取的desired_features結構的相應成員是否設定為1來執行此操作。 清除其餘的desired_features結構成員(將它們設定為零)。 8.根據屬性(支援的操作功能型別),準備一個佇列族列表,從中請求佇列。準備每個選定的佇列族系列請求的多個佇列。為給定組中的每個佇列分配優先順序:浮點值從0.0f1.0f(多個佇列可能具有相同的優先順序)。使用自定義型別QueueInfo的元素建立名為queue_infosstd::vector變數。講佇列族的索引和優先順序列表儲存在queue_infos向量中,優先順序向量的大小應該等於每個族的佇列數。 9.準備名為queue_create_infosstd::vector<VkDeviceQueueCreateInfo>型別變數,將儲存在queue_infos變數中的每個佇列族新增到queue_create_infos向量中,為新元素的成員分配以下值:     1.sType設為VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO。     2.pNext設為nullptr。     3.flags設為0.     4.queueFamilyIndex設為族的索引。     5.queueCount設為族的佇列數。     6.pQueuePriorities為指向給定族的佇列優先順序列表的第一個元素的指標。 10.建立名為device_create_infoVkDeviceCreateInfo型別的變數。為device_create_info變數的成員分配以下值:     1.sType設為VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO。     2.pNext設為nullptr。     3.flags設為0。     4.queueCreateInfoCount設為queue_create_infos的元素數。     5.pQueueCreateInfos設為指向queue_create_infos向量第一個元素。     6.enabledLayerCount設為0.     7.ppEnabledLayerNames設為nullptr。     8.enabledExtensionCount設為desired_extensions的元素數。     9.ppEnabledExtensionNames設為指向desired_extensions向量的第一個元素(如果為空則為nullptr)。     10.pEnabledFeatures指向desired_features變數。 11.準備名為logical_deviceVkDevice型別變數。 12.呼叫vkCreateDevice(physical_device, &device_create_info, nullptr, &logical_device)。第一個引數為物理裝置的控制代碼,第二個引數指向device_create_info變數,第三個引數設為nullptr,第四個引數指向logical_device變數。 13.檢查呼叫vkCreateDevice()函式返回的值是否等於VK_SUCCESS來確保操作成功。

這個怎麼運作...

要建立邏輯裝置,我們需要準備大量資料。 首先,我們需要獲取給定物理裝置支援的擴充套件列表,然後我們需要檢查我們要啟用的所有擴充套件是否都可以在支援的擴充套件列表中找到。 與例項建立類似,我們無法建立不受支援擴充套件的邏輯裝置。 這樣的操作將失敗:

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;