Skip to content

Commit

Permalink
merge dev to main
Browse files Browse the repository at this point in the history
  • Loading branch information
FuXiii committed Feb 4, 2024
2 parents ecf7d66 + aeb6a13 commit 15780b9
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 24 deletions.
9 changes: 9 additions & 0 deletions source/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
此更新日志为纵览更新,对于具体文章的更新位于每个文章的开头的 `更新记录` 中。
```

## 2024/2/4

>* 更新`设备队列`文档
>* 更新`纵览`文档
## 2024/2/3

>* 更新`设备队列`文档
## 2024/2/2

>* 更新`最初之物 VkInstance`文档
Expand Down
133 changes: 132 additions & 1 deletion source/DeviceQueue.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,135 @@
:color: muted
:icon: history

* 2024/1/2 增加该文档
* 2024/1/2 增加该文档。
* 2024/2/3 更新该文档。
* 2024/2/3 增加 ``vkGetPhysicalDeviceQueueFamilyProperties`` 章节。
* 2024/2/3 增加 ``VkQueueFamilyProperties`` 章节。
* 2024/2/4 更新 ``VkQueueFamilyProperties`` 章节。

在 ``Vulkan`` 标准中,物理设备在其内部为我们开放了一系列的 ``工作队列`` (设备队列),不同系列的工作队列会接收并执行不同的指令,并且会根据这些 ``工作队列`` 的特长(功能)进行分组(族)。可分为以下 ``5`` 种:

* :bdg-secondary:`图形` 主要用于图形渲染,执行各种渲染绘制指令。
* :bdg-secondary:`计算` 主要用于执行并行计算(计算着色器),执行各种计算指令。
* :bdg-secondary:`转移` 主要用于执行资源的布局转移并支持在不同队列中进行转移,执行各种转移指令。
* :bdg-secondary:`稀疏绑定` 主要用于稀疏内存的管理。
* :bdg-secondary:`受保护` 主要用于受保护的内存的管理。

.. admonition:: 重要
:class: important

一般 :bdg-danger:`率先` 需要 ``图形`` 功能的队列。有如下几点原因:

* 需要使用该队列进行图形渲染
* 如果队列支持 ``图形`` 功能,则该队列默认也支持 ``计算`` 和 ``转移`` 功能。

我们可以通过 ``vkGetPhysicalDeviceQueueFamilyProperties(...)`` 函数获取设备队列(族)信息,其定义如下:

vkGetPhysicalDeviceQueueFamilyProperties
##############################################

.. code:: c++

// 由 VK_VERSION_1_0 提供
void vkGetPhysicalDeviceQueueFamilyProperties(
VkPhysicalDevice physicalDevice,
uint32_t* pQueueFamilyPropertyCount,
VkQueueFamilyProperties* pQueueFamilyProperties);

* :bdg-secondary:`physicalDevice` 物理设备。
* :bdg-secondary:`pQueueFamilyPropertyCount` 表示 ``pQueueFamilyProperties`` 中的元素个数。
* :bdg-secondary:`pQueueFamilyProperties` 如果为 ``nunllptr`` ,将会向 ``pQueueFamilyPropertyCount`` 中写入 ``physicalDevice`` 中对外开放的设备队列数量。否则将会写入 ``pQueueFamilyPropertyCount`` 个设备队列族信息数据。

为了获取设备队列信息,我们需要调用两次该函数,这与之前其他获取信息函数类似,不在过多赘述。

.. code:: c++

VkPhysicalDevice physical_device = 之前获取的物理设备;

uint32_t queue_family_property_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_property_count, nullptr);

std::vector<VkQueueFamilyProperties> queue_family_properties(queue_family_property_count);
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_property_count, queue_family_properties.data());

该函数将物理设备的一系列设备队列(族)信息写入了 ``VkQueueFamilyProperties`` 类型数组当中,该类型定义如下:

VkQueueFamilyProperties
##############################################

.. code:: c++

// 由 VK_VERSION_1_0 提供
typedef struct VkQueueFamilyProperties {
VkQueueFlags queueFlags;
uint32_t queueCount;
uint32_t timestampValidBits;
VkExtent3D minImageTransferGranularity;
} VkQueueFamilyProperties;

* :bdg-secondary:`queueFlags` 为队列族位域,用于描述该队列族支持的功能。
* :bdg-secondary:`queueCount` 该队列族中的队列数量。最起码有 ``1`` 个, :bdg-danger:`不会` 返回 ``0`` 。
* :bdg-secondary:`timestampValidBits` 时间戳中有效的位数,有效的位数范围为 ``36`` 到 ``64`` 位,如果为 ``0`` 说明不支持时间戳。超出有效范围的位保证为 ``0`` 。
* :bdg-secondary:`minImageTransferGranularity` 在该族队列上进行图片转移操作时支持的最小转移粒度(大小)。

``Vulkan`` 将设备队列按照队列族的方式组织,组织方式有如下特点:

* 一个队列族可以支持一到多个功能。
* 一个队列族中包含一个或多个队列。
* 同一个队列族中的所有队列支持相同的功能。
* 队列族之间可以有相同的功能,但队列族之间两两不能有完全相同的功能集。

其中 ``VkQueueFlags`` 可用的值定义在 ``VkQueueFlagBits`` 中,其定义如下:

.. code:: c++

// 由 VK_VERSION_1_0 提供
typedef enum VkQueueFlagBits {
VK_QUEUE_GRAPHICS_BIT = 0x00000001,
VK_QUEUE_COMPUTE_BIT = 0x00000002,
VK_QUEUE_TRANSFER_BIT = 0x00000004,
VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008,
// 由 VK_VERSION_1_1 提供
VK_QUEUE_PROTECTED_BIT = 0x00000010,
} VkQueueFlagBits;

* :bdg-secondary:`VK_QUEUE_GRAPHICS_BIT` 支持 ``图形`` 功能。
* :bdg-secondary:`VK_QUEUE_COMPUTE_BIT` 支持 ``计算`` 功能。
* :bdg-secondary:`VK_QUEUE_TRANSFER_BIT` 支持 ``转移`` 功能。
* :bdg-secondary:`VK_QUEUE_SPARSE_BINDING_BIT` 支持 ``稀疏绑定`` 功能。
* :bdg-secondary:`VK_QUEUE_PROTECTED_BIT` 支持 ``受保护`` 功能。

示例
###############

.. code:: c++

VkPhysicalDevice physical_device = 之前获取到的物理设备句柄;

uint32_t queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr);

std::vector<VkQueueFamilyProperties> queue_familys(queue_family_count);
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_familys.data());

uint32_t uint32_max = std::numeric_limits<uint32_t>::max();
uint32_t support_graphics_queue_family_index = UINT32_MAX;
for(uint32_t index = 0; index < queue_family_count ; index++)
{
if((queue_familys[index].queueFlags & VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT) == VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT)
{
// 寻找支持图形的队列族
support_graphics_queue_family_index = index;
break;
}
}

if(support_graphics_queue_family_index == UINT32_MAX)
{
throw std::runtime_error("没找到支持图形的队列族");
}

.. admonition:: support_graphics_queue_family_index
:class: important

需要获取存储对应设备队列族在 ``VkQueueFamilyProperties`` 数组中的索引值,这会在之后创建 `逻辑设备 <./LogicDevice.html>`_ 时指定设备队列时要用到。
118 changes: 95 additions & 23 deletions source/Overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@
* 2024/1/20 增加 ``VkFenceCreateInfo`` 章节。
* 2024/1/20 增加 ``等待栏栅`` 章节。
* 2024/1/20 增加 ``vkWaitForFences`` 章节。
* 2024/2/4 增加 ``VkInstanceCreateFlags`` 章节。并增加 ``VkFlags 与 位域`` 说明。
* 2024/2/4 更新 ``VkQueueFlags`` 章节。将其中的 ``VkFlags`` 说明转移至 ``VkInstanceCreateFlags`` 章节的 ``VkFlags 与 位域`` 说明中。

由于 ``Vulkan`` 比较复杂,为了更好的入门 ``Vulkan`` ,还是大致过一遍 ``Vulkan`` 的核心思路,这对以后的学习很有帮助。

Expand Down Expand Up @@ -682,6 +684,99 @@ VkInstanceCreateInfo
...
}

其中 ``VkInstanceCreateFlags`` 定义入下:

VkInstanceCreateFlags
------------------------

.. code:: c++

// 由 VK_VERSION_1_0 提供
typedef VkFlags VkInstanceCreateFlags;

.. code:: c++

// 由 VK_VERSION_1_0 提供
typedef uint32_t VkFlags;

从定义可知 ``VkFlags`` 是一个 ``32`` 为无符号整数。

其中 ``VkInstanceCreateFlags`` 可以设置的有效值定义在了 ``VkInstanceCreateFlagBits`` 中,如下:

.. code:: c++

// 由 VK_VERSION_1_0 提供
typedef enum VkInstanceCreateFlagBits {
// 由 VK_KHR_portability_enumeration 扩展提供
VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR = 0x00000001,
} VkInstanceCreateFlagBits;

其中 ``VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR`` 只有在开启 ``VK_KHR_portability_enumeration`` 扩展后才能使用,这里可以忽略。这里引出 ``VkInstanceCreateFlagBits`` 是想说明如下问题:

* 在 ``Vulkan`` 中提供了各种各样的 ``VkFlags`` 的 ``typedef Vk{标志位类型名称}Flags`` 别名,用于配置各种额外信息。
* ``Vk{标志位类型名称}Flags`` 中可用的值都对应定义在 ``Vk{标志位类型名称}FlagBits`` 枚举类型中。
* ``VkFlags`` 为 ``32`` 位无符号整型,可以存储 ``32`` 个比特位。
* ``Vk{标志位类型名称}FlagBits`` 中定义了每个比特位所对应的含义。

.. admonition:: VkFlags 与 位域
:class: note

像 ``VkFlags`` 这样的使用比特位存储数据的形式叫做 ``位域`` 或 ``标志位`` 。每一位都代表某个特殊含义。而 ``Vk{标志位类型名称}FlagBits`` 中恰恰定义了每一位,所以其中的每一个枚举值必须为 :math:`2^n` 。

像 ``uint32_t`` ,其比特位有 ``32`` 个,如果某一比特位为 ``1`` 则说明对应的位域被激活,也就是对应位域表示的事物被激活。比如:

.. code:: c++

uint32_t LIKE_CAT_BIT = 0x1; //对应的二进制:01
uint32_t LIKE_DOG_BIT = 0x2; //对应的二进制:10

uint32_t likes = 某人的喜好;

if(likes == 0) //什么也不喜欢
if((likes & LIKE_CAT_BI) == LIKE_CAT_BIT) //喜欢猫
if((likes & LIKE_DOG_BIT) == LIKE_DOG_BIT) //喜欢狗
if((likes & (LIKE_CAT_BIT | LIKE_DOG_BIT)) == (LIKE_CAT_BIT | LIKE_DOG_BIT)) //既喜欢猫,也喜欢狗

``Vulkan`` 中的标志位也沿用了这套方式,比如 ``VkColorComponentFlags`` 颜色通道标志字段:

.. code:: c++

// 由 VK_VERSION_1_0 提供
typedef VkFlags VkColorComponentFlags;

可用的位域定义在了 ``VkColorComponentFlagBits`` 中,如下:

.. code:: c++

// 由 VK_VERSION_1_0 提供
typedef enum VkColorComponentFlagBits {
VK_COLOR_COMPONENT_R_BIT = 0x00000001,
VK_COLOR_COMPONENT_G_BIT = 0x00000002,
VK_COLOR_COMPONENT_B_BIT = 0x00000004,
VK_COLOR_COMPONENT_A_BIT = 0x00000008,
} VkColorComponentFlagBits;

* :bdg-secondary:`VK_COLOR_COMPONENT_R_BIT` 红色通道。十六进制为 ``0x1`` ,对应的二进制为 ``0001`` 。
* :bdg-secondary:`VK_COLOR_COMPONENT_G_BIT` 绿色通道。十六进制为 ``0x2`` ,对应的二进制为 ``0010`` 。
* :bdg-secondary:`VK_COLOR_COMPONENT_B_BIT` 蓝色通道。十六进制为 ``0x4`` ,对应的二进制为 ``0100`` 。
* :bdg-secondary:`VK_COLOR_COMPONENT_A_BIT` 透明通道。十六进制为 ``0x8`` ,对应的二进制为 ``1000`` 。

在位域中一般直接使用 ```` 操作符,也就是与、或、非、异或。

.. code:: c++

VkColorComponentFlags color_component_flags = 0;
color_component_flags = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT; // 表示开启红色和绿色通道
color_component_flags = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT; // 表示开启红色、绿色和蓝色通道
color_component_flags = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; // 表示开启红色、绿色、蓝色和透明通道

bool is_open_r = (color_component_flags & VK_COLOR_COMPONENT_R_BIT) == VK_COLOR_COMPONENT_R_BIT ? true : false;
bool is_open_g = (color_component_flags & VK_COLOR_COMPONENT_G_BIT) == VK_COLOR_COMPONENT_G_BIT ? true : false;
bool is_open_b = (color_component_flags & VK_COLOR_COMPONENT_B_BIT) == VK_COLOR_COMPONENT_B_BIT ? true : false;
bool is_open_a = (color_component_flags & VK_COLOR_COMPONENT_A_BIT) == VK_COLOR_COMPONENT_A_BIT ? true : false;

这样就可以使用一个 ``32`` 位整数的每一位,表示不同的含义。最直接的好处就是节省内存。

VkApplicationInfo
----------------------

Expand Down Expand Up @@ -1126,33 +1221,10 @@ VkQueueFlags

.. code:: c++

typedef uint32_t VkFlags;
typedef VkFlags VkQueueFlags;

可以看到 ``VkQueueFlags`` 其实就是一个 ``uint32_t`` 的标志位。

.. admonition:: VkFlags
:class: note

在 ``Vulkan`` 中所有的标志位 ``Vk{标志位名称}Flags`` 都为 ``VkFlags`` 也就是 ``uint32_t`` 。每一位对应的含义都在对应的 ``Vk{标志位名称}FlagBits`` 枚举中定义。

.. admonition:: 标志位与位域
:class: note

所谓标志位,也就是位域。像 ``uint32_t`` 其比特位有 ``32`` 个,如果某一比特位为 ``1`` 则说明对应的位域被激活,也就是对应位域表示的事物被激活。比如:

.. code:: c++

uint32_t LIKE_CAT_BIT = 0x00000001; //对应的二进制:0000 0000 0000 0000 0000 0000 0000 0001
uint32_t LIKE_DOG_BIT = 0x00000002; //对应的二进制:0000 0000 0000 0000 0000 0000 0000 0010

uint32_t likes = 某人的喜好;

if(likes == 0) //什么也不喜欢
if((likes & LIKE_CAT_BI) == LIKE_CAT_BIT) //喜欢猫
if((likes & LIKE_DOG_BIT) == LIKE_DOG_BIT) //喜欢狗
if((likes & (LIKE_CAT_BIT | LIKE_DOG_BIT)) == (LIKE_CAT_BIT | LIKE_DOG_BIT)) //既喜欢猫,也喜欢狗

``VkQueueFlags`` 对应位域的 ``VkQueueFlagBits`` 定义如下:

VkQueueFlagBits
Expand Down

0 comments on commit 15780b9

Please sign in to comment.