[譯]Vulkan教程(07)物理設備和隊列家族


[譯]Vulkan教程(07)物理設備和隊列家族

Selecting a physical device 選擇一個物理設備

After initializing the Vulkan library through a VkInstance we need to look for and select a graphics card in the system that supports the features we need. In fact we can select any number of graphics cards and use them simultaneously, but in this tutorial we'll stick to the first graphics card that suits our needs.

通過VkInstance 初始化了Vulkan庫之后,我們需要在系統中查找和選擇一個支持我們需要的特性的圖形卡。實際上我們可以選擇任意多個圖形卡,同步地使用它們,但是本教程中我們只使用第一個滿足我們需要的圖形卡。

We'll add a function pickPhysicalDevice and add a call to it in the initVulkan function.

我們添加一個函數pickPhysicalDevice ,在initVulkan 函數中調用它。

1 void initVulkan() {
2     createInstance();
3     setupDebugCallback();
4     pickPhysicalDevice();
5 }
6  
7 void pickPhysicalDevice() {
8  
9 }

 

The graphics card that we'll end up selecting will be stored in a VkPhysicalDevice handle that is added as a new class member. This object will be implicitly destroyed when the VkInstance is destroyed, so we won't need to do anything new in the cleanup function.

我們添加一個VkPhysicalDevice 成員,用於保存我們將選擇的圖形卡。這個對象會在VkInstance 被銷毀時隱式地銷毀,所以我們不需要在cleanup 函數中做什么。

VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;

 

Listing the graphics cards is very similar to listing extensions and starts with querying just the number.

列舉圖形卡,與列舉擴展很相似,開始要查詢數量。

1 uint32_t deviceCount = 0;
2 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);

 

If there are 0 devices with Vulkan support then there is no point going further.

如果支持Vulkan的設備數量為0,那么就沒有必要繼續了。

1 if (deviceCount == 0) {
2     throw std::runtime_error("failed to find GPUs with Vulkan support!");
3 }

 

Otherwise we can now allocate an array to hold all of the VkPhysicalDevice handles.

如果有,我們就申請一個數組來記錄所有的VkPhysicalDevice 句柄。

1 std::vector<VkPhysicalDevice> devices(deviceCount);
2 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());

 

Now we need to evaluate each of them and check if they are suitable for the operations we want to perform, because not all graphics cards are created equal. For that we'll introduce a new function:

現在我們需要評估它們,檢查它們是不是適合我們需要施展的操作,因為不是所有的圖形卡都一樣。為此我們引入一個新的函數:

1 bool isDeviceSuitable(VkPhysicalDevice device) {
2     return true;
3 }

 

And we'll check if any of the physical devices meet the requirements that we'll add to that function.

我們將檢查設備是否符合我們在此函數中設定的要求。

 1 for (const auto& device : devices) {
 2     if (isDeviceSuitable(device)) {
 3         physicalDevice = device;
 4         break;
 5     }
 6 }
 7  
 8 if (physicalDevice == VK_NULL_HANDLE) {
 9     throw std::runtime_error("failed to find a suitable GPU!");
10 }

 

The next section will introduce the first requirements that we'll check for in the isDeviceSuitable function. As we'll start using more Vulkan features in the later chapters we will also extend this function to include more checks.

下一節將引入第一個要求,我們將在isDeviceSuitable 函數中檢查它。隨着我們將使用更多的Vulkan特性,我們還會逐步擴展這個函數,加入更多的檢查。

Base device suitability checks 基礎的設備適用性檢查

To evaluate the suitability of a device we can start by querying for some details. Basic device properties like the name, type and supported Vulkan version can be queried using vkGetPhysicalDeviceProperties.

為了估計設備的適用性,我們可以從查詢某些細節開始。基礎的設備屬性(例如名字、類型和支持的Vulkan版本)可以用vkGetPhysicalDeviceProperties查詢。

1 VkPhysicalDeviceProperties deviceProperties;
2 vkGetPhysicalDeviceProperties(device, &deviceProperties);

 

The support for optional features like texture compression, 64 bit floats and multi viewport rendering (useful for VR) can be queried using vkGetPhysicalDeviceFeatures:

對於可選特性(例如紋理壓縮、64位浮點數和多視口渲染(用於VR))的支持,可以用vkGetPhysicalDeviceFeatures查詢。

VkPhysicalDeviceFeatures deviceFeatures;
vkGetPhysicalDeviceFeatures(device, &deviceFeatures);

 

There are more details that can be queried from devices that we'll discuss later concerning device memory and queue families (see the next section).

關於設備內存和隊列家族(見下一節)方面,設備還有更多細節可供查詢,我們將在以后討論之。

As an example, let's say we consider our application only usable for dedicated graphics cards that support geometry shaders. Then the isDeviceSuitable function would look like this:

舉個例子,假設我們的app只使用支持幾何shader的專用圖形卡。那么isDeviceSuitable 函數應該是這樣:

bool isDeviceSuitable(VkPhysicalDevice device) {
    VkPhysicalDeviceProperties deviceProperties;
    VkPhysicalDeviceFeatures deviceFeatures;
    vkGetPhysicalDeviceProperties(device, &deviceProperties);
    vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
 
    return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
           deviceFeatures.geometryShader;
}

 

Instead of just checking if a device is suitable or not and going with the first one, you could also give each device a score and pick the highest one. That way you could favor a dedicated graphics card by giving it a higher score, but fall back to an integrated GPU if that's the only available one. You could implement something like that as follows:

你可以檢查一個設備適用與否,然后總選第一個合適的,你更可以給每個設備一個評分,選擇最高分的。那樣你就可以通過最高分來找到最專業的圖形卡,而若只有1個適用的,你也可以選到那個。你可以實現一些這樣的東西:

#include <map>
 
...
 
void pickPhysicalDevice() {
    ...
 
    // Use an ordered map to automatically sort candidates by increasing score
    std::multimap<int, VkPhysicalDevice> candidates;
 
    for (const auto& device : devices) {
        int score = rateDeviceSuitability(device);
        candidates.insert(std::make_pair(score, device));
    }
 
    // Check if the best candidate is suitable at all
    if (candidates.rbegin()->first > 0) {
        physicalDevice = candidates.rbegin()->second;
    } else {
        throw std::runtime_error("failed to find a suitable GPU!");
    }
}
 
int rateDeviceSuitability(VkPhysicalDevice device) {
    ...
 
    int score = 0;
 
    // Discrete GPUs have a significant performance advantage
    if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
        score += 1000;
    }
 
    // Maximum possible size of textures affects graphics quality
    score += deviceProperties.limits.maxImageDimension2D;
 
    // Application can't function without geometry shaders
    if (!deviceFeatures.geometryShader) {
        return 0;
    }
 
    return score;
}

 

You don't need to implement all that for this tutorial, but it's to give you an idea of how you could design your device selection process. Of course you can also just display the names of the choices and allow the user to select.

在本教程中你不需要實現所有這些,但這些可以給你一點啟示(關於如何設計你的設備選擇流程)。當然你可以只顯示選項名字,讓用戶來選擇。

Because we're just starting out, Vulkan support is the only thing we need and therefore we'll settle for just any GPU:

因為我們是剛剛開始,Vulkan支持是我們唯一需要的,因此我們對任何GPU都能行:

bool isDeviceSuitable(VkPhysicalDevice device) {
    return true;
}

 

In the next section we'll discuss the first real required feature to check for.

下一節我們將討論要檢查的第一個特性。

Queue families 隊列家族

It has been briefly touched upon before that almost every operation in Vulkan, anything from drawing to uploading textures, requires commands to be submitted to a queue. There are different types of queues that originate from different queue families and each family of queues allows only a subset of commands. For example, there could be a queue family that only allows processing of compute commands or one that only allows memory transfer related commands.

之前簡要提到過,Vulkan中的任何操作,從繪畫到上傳紋理,都需要將命令提交到一個隊列。有多種類型的queue,它們起源於不同的隊列家族,每個家族里的隊列都只能做某一些特定的命令。例如,可能有一個隊列家族,它只能處理計算命令,或者只能執行內存轉移相關的命令。

We need to check which queue families are supported by the device and which one of these supports the commands that we want to use. For that purpose we'll add a new function findQueueFamilies that looks for all the queue families we need. Right now we'll only look for a queue that supports graphics commands, but we may extend this function to look for more at a later point in time.

我們需要檢查,設備支持哪些隊列家族,其中哪個支持我們想使用的命令。為此,我們添加新函數findQueueFamilies ,它查詢我們需要的所有的隊列家族。現在我們將只查找支持圖形命令的隊列,但是我們可能以后擴展這個函數,以查詢更多東西。

This function will return the indices of the queue families that satisfy certain desired properties. The best way to do that is using a structure where we use std::optional to track if an index was found:

這個函數會返回滿足給定屬性的隊列家族的索引。最好的方式是用一個struct(我們用std::optional )來追蹤一個索引是否被找到了:

struct QueueFamilyIndices {
    std::optional<uint32_t> graphicsFamily;
 
    bool isComplete() {
        return graphicsFamily.has_value();
    }
};

 

Note that this also requires including <optional>. We can now begin implementing findQueueFamilies:

注意這需要include一下<optional>。現在我們可以開始實現findQueueFamilies了:

QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
    QueueFamilyIndices indices;
 
    ...
 
    return indices;
}

 

The process of retrieving the list of queue families is exactly what you expect and uses vkGetPhysicalDeviceQueueFamilyProperties:

如你所料,檢索家族列表的過程,就是要用vkGetPhysicalDeviceQueueFamilyProperties

uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
 
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());

 

The VkQueueFamilyProperties struct contains some details about the queue family, including the type of operations that are supported and the number of queues that can be created based on that family. We need to find at least one queue family that supports VK_QUEUE_GRAPHICS_BIT.

結構體VkQueueFamilyProperties 包含隊列家族的一些細節,包括支持的操作類型,可以創建的隊列數量。我們需要找到至少一個支持VK_QUEUE_GRAPHICS_BIT的隊列家族。

int i = 0;
for (const auto& queueFamily : queueFamilies) {
    if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
        indices.graphicsFamily = i;
    }
 
    if (indices.isComplete()) {
        break;
    }
 
    i++;
}

 

Now that we have this fancy queue family lookup function, we can use it as a check in the isDeviceSuitablefunction to ensure that the device can process the commands we want to use:

現在我們有了查詢隊列家族的函數,我們可以在isDeviceSuitable函數中用它做個判斷,確保設備能處理我們想用的命令:

bool isDeviceSuitable(VkPhysicalDevice device) {
    QueueFamilyIndices indices = findQueueFamilies(device);
 
    return indices.isComplete();
}

 

Great, that's all we need for now to find the right physical device! The next step is to create a logical device to interface with it.

好極了,這就是我們目前需要的用以找到正確的物理設備的所有東西!下一步是創建邏輯設備,然后與之交互。

C++ code C++代碼

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM