内容
图片说明
表格说明
程序片段清单
关于本书
关于示例代码
错误纠正
致谢
关于作者
第一章 Vulkan概述
1.1 介绍
1.2 实例,设备和队列
1.2.1 Vulkan实例
1.2.2 Vulkan物理设备
1.2.3 物理设备内存
1.2.4 设备队列
1.2.5 创建逻辑设备
1.3 对象类型和函数约定
1.4 管理内存
1.5 Vulkan的多线程
1.6 数学概念
1.6.1 向量和矩阵
1.6.2 坐标系
1.7 Vulkan增强机制
1.7.1 图层
1.7.2 扩展
1.8 关闭清理程序
本章总结
第二章 内存和资源
2.1 主机内存管理
2.2 资源
2.2.1 缓冲流
2.2.2 格式和支持
2.2.3 图片
2.2.4 资源视图
2.2.5 销毁资源
2.3 设备内存管理
2.3.1 分配设备内存
2.3.2 主机访问设备内存
2.3.3 为资源绑定内存
2.3.4 稀疏资源
本章总结
第三章 队列和命令
3.1 设备队列
3.2 创建命令缓冲区
3.3 记录存储命令
3.4 回收命令缓冲区
3.5 提交命令
本章总结
第四章 数据操作
4.1 管理资源状态
4.1.1 管道障碍
4.1.2 全局内存障碍
4.1.3 缓冲区内存障碍
4.1.4 图像内存障碍
4.2 清除和填充缓存流
4.3 清除和填充图像
4.4 复制图像数据
4.5 复制压缩的图像数据
4.6 拉伸图像
本章总结
第五章 呈现
5.1 呈现扩展
5.2 呈现表面
5.1.1 在Microsoft Windows上的呈现
5.1.2 基于Xlib的平台呈现
5.1.3 用Xcb呈现
5.3 交换链
5.4 全屏表面
5.5 实现呈现
5.6 清理程序
本章总结
第六章 着色器和管道
6.1 GLSL概述
6.2 SPIR-V概述
6.2.1 SPIR-V的表示
6.2.2 将SPIR-V送到Vulkan
6.3 管道
6.3.1 计算管道
6.3.2 创建管道
6.3.3 专业常数
6.3.4 加速管道创建
6.3.5 绑定管道
6.4 执行工作
6.5 着色器中的资源访问
6.5.1 描述符集
6.5.2 将资源绑定到描述符集
6.5.3 绑定描述符集
6.5.4 统一,纹理单元和存储缓冲区
6.5.5 推送常量
6.5.6 抽样图像
本章总结
第七章 图形管道
7.1 逻辑图形管道
7.2 Renderpasses
7.3 帧缓冲
7.4 创建简单的图形管道
7.4.1 图形着色阶段
7.4.2 顶点输入状态
7.4.3 输入装配
7.4.4 镶嵌状态
7.4.5 视口状态
7.4.6 栅格化状态
7.4.7 多重采样状态
7.4.8 深度和钢板状态
7.4.9 颜色混合状态
7.5 动态状态
本章总结
第八章 绘图
8.1 准备绘制
8.2 顶点数据
8.3 索引绘制
8.4 仅索引渲染
8.5 重置索引
8.6 实例化
8.7 间接绘制
本章总结
第九章 几何处理
9.1 细分
9.1.1 细分配置
9.1.2 细分变量
9.1.3 细分示例:置换映射
9.2 几何着色器
9.2.1 切割原始数据
9.2.2 几何着色器实例化
9.3 可编程点大小
9.4 线宽和栅格化
9.5 用户剪切和拣选
9.6 视口转换
本章总结
第十章 片段处理
10.1 剪刀测试
10.2 深度操作和模板操作
10.2.1 深度测试
10.2.2 模板测试
10.2.3 早期片段测试
10.3 多重采样渲染
10.3.1 采样率阴影
10.3.2 多重采样解析
10.4 逻辑运算
10.5 片段着色器输出
10.6 颜色混合
本章总结
第十一章 同步
11.1 围栏
11.2 事件
11.3 信号量
第十二章 获取数据
12.1 查询
12.1.1 执行查询
12.1.2 时序查询
12.2 通过主机读取数据
本章总结
第十三章 多线渲染
13.1 输入附件
13.2 附件内容
13.2.1 附件初始化
13.2.2 渲染区域
13.2.3 保留附件内容
13.3 辅助命令缓冲区
本章总结
附录:Vulkan函数
词汇表
索引
图
图示1.1 Vulkan中实例,设备和队列的层次结构
图示2.1 Mipmap图片布局
图示2.2 线性平铺图片的内存布局
图示2.4 立体贴图的构造
图示2.5 主机与设备内存
图示4.1 存储在缓冲区中的图像的数据布局
图示6.1 描述符集与管道集
图示6.2 线性采样
图示6.3 不同采样模式的效果
图示7.1 完整的Vulken图形管道
图示 7.2 带状(左)与扇形(右)拓扑
图示 7.3 带有邻接拓扑的三角形
图示 7.4 带有邻接拓扑的三角带
图示8.1 索引数据流
图示8.2 不同索引模式对三角形带的影响
图示 8.3 多个立方体示例
图示 9.1 四边形细分
图示 9.2 三角形细分
图示 9.3 等值线细分
图示 9.4 细分空间模式
图示 9.5 细分位移映射的结果
图示9.6 严格化直线的光栅化
图示9.7 非严格化直线的光栅化
图示9.8 靠近视图的图形裁剪
图示10.1 标准采样位置
图示13.1 一个简单的延迟渲染器的数据流向
图示13.2 半透明几何图形对不透明几何图形的系列依赖
图示 13.3 半透明和不透明几何的平行渲染
表
表2.1 稀疏纹理块大小
表6.1 管道资源限制
表6.2 纹理比较函数
表7.1 动态和静态状态有效性
表9.1 GLSL和SPIR-V细分模式
表9.2 GLSL和SPIR-V曲面细分绕组顺序
表10.1 深度比较函数
表10.2 模板操作
表10.3 逻辑操作
表10.4 混合方程
表10.5 混合因子
代码清单
清单1.1 创建Vulkan实例
清单1.2 查询物理设备属性
清单1.3 创建逻辑设备
清单1.4 查询实例层
清单1.5 查询实例扩展
清单2.1 内存分配器类的声明
清单2.2 内存分配器类的实现
清单2.3 创建缓冲区对象
清单2.4 创建图像对象
清单2.5 为映像选择内存类型
清单3.1 使用vkCmdCopyBuffer()的示例
清单4.1图像内存瓶颈
清单4.2 使用浮点数据填充缓冲区
清单5.1 创建交换链
清单5.2 将图像转换到Present Source
清单6.1 最简单的GLSL着色器
清单6.2 最简单的SPIR-V
清单6.3 计算着色器(GLSL)中的局部大小声明
清单6.4 计算着色器中的本地大小声明(SPIR-V)
清单6.5 GLSL中的专业化常量
清单6.6 SPIR-V中的专业化常量
清单6.7 将管道缓存数据保存到文件
清单6.8 声明GLSL中的资源
清单6.9 在SPIR-V中声明资源
清单6.10 创建管道布局
清单6.11 在GLSL中声明均匀和着色器块
清单6.12 在SPIR-V中声明统一和着色器模块
清单6.13 在GLSL中声明Texel缓冲区
清单6.14 在SPIR-V中声明Texel缓冲区
清单6.15 在GLSL中声明推送常量
清单6.16 在SPIR-V中声明推送常量
清单7.1创建一个简单的Renderpass
清单7.2 创建简单图形管道
清单7.3描述顶点输入数据
清单7.4声明顶点着色器(GLSL)的输入
清单7.5声明顶点着色器的输入(SPIR-V)
清单8.1 分离的顶点属性设置
清单8.2 带索引的立体数据
清单8.3 在着色器中使用顶点索引
清单8.4 在着色器中使用实例索引
清单8.5 在着色器中使用绘制索引
清单9.1 平凡细分控制着色器(GLSL)
清单9.2 平凡细分控制着色器(SPIR-V)
清单9.3 在Tessellation控制着色器(GLSL)中声明输出
清单9.4 在Tessellation控制着色器中声明输出(SPIR-V)
清单9.5 在评估着色器(GLSL)中访问gl_TessCoord
清单9.6 在评估着色器(SPIR-V)中访问gl_TessCoord
清单9.7 位移映射的描述符设置
清单9.8 顶点着色器的位移映射
清单9.9 用于位移映射的Tessellation控制着色器
清单9.10 用于位移映射的Tessellation评估着色器
清单9.11 细分状态创建信息
清单9.12 最小几何着色器(GLSL)
清单9.13 最小几何着色器(SPIR-V)
清单9.14 在GLSL几何着色器中声明gl_PerVertex
清单9.15 在SPIR-V几何着色器中读取gl_PerVertex
清单9.16 在GLSL中声明输出块
清单9.17 传递GLSL几何着色器
清单9.18 传递SPIR-V几何着色器
清单9.19 几何着色器中的切割条
清单9.20 实例化GLSL几何着色器
清单9.21 在GLSL中使用gl_PointSize
清单9.22 用PointSize装饰输出
清单9.23 在GLSL中使用gl_ClipDistance
清单9.24 使用ClipDistance装饰输出
清单9.25 在GLSL中使用gl_CullDistance
清单9.26 使用CullDistance装饰输出
清单9.27 在几何着色器(GLSL)中使用多个视口
清单10.1 在片段着色器(GLSL)中声明输出
清单10.2 在片段着色器(SPIR-V)中声明输出
清单10.3 片段着色器中的几个输出(GLSL)
清单10.4 片段着色器中的几个输出(SPIR-V)
清单11.1 四域同步的设置
清单11.2 同步栅栏上的循环等待
清单11.3 带有信号量的跨队列提交
清单12.1 所有流水线统计的C结构
清单12.2 将缓冲区移动到主机可读状态
清单13.1 延迟着色Renderpass设置
清单13.2 半透明和延迟着色设置
关于本书
关于示例代码
关于错误纠正
致谢
关于作者
本章你将学习到:
•Vulkan是什么,它的基本原理
•如何创建最小的Vulkan应用程序
•本书其余部分中使用的术语和概念
在本章中,我们介绍Vulkan并解释它是什么。 我们介绍了API背后的一些基本概念,包括初始化,对象生命周期,Vulkan实例以及逻辑设备和物理设备。 到本章结束,我们将生成一个简单的Vulkan应用程序,它可以初始化Vulkan系统,发现可用的Vulkan设备并显示其属性和功能,最后彻底关闭。
Vulkan介绍
Vulkan是用于图形和计算设备的编程接口。 Vulkan设备通常由处理器和多个固定功能硬件块组成,以加速在图形和计算中使用的操作。设备中的处理器通常是一个非常宽的多线程处理器,因此Vulkan中的计算模型主要基于并行计算。 Vulkan设备还可以访问可能与您的应用程序运行所在的主处理器共享或可能不共享的内存。 Vulkan也暴露了这个内存给你。
Vulkan是一个显式的API。也就是说,几乎一切都是你的责任。驱动程序是一个软件,它接收形成API的命令和数据,并将它们转换为硬件可以理解的东西。在较旧的API(如OpenGL)中,驱动程序将跟踪许多对象的状态,为您管理内存和同步,并在运行时检查应用程序中的错误。这对开发人员非常有用,但是一旦应用程序调试和运行正常,就会浪费宝贵的CPU时间。 Vulkan通过将几乎所有状态跟踪,同步和内存管理置于应用程序开发人员手中,并通过对必须启用的层委派正确性的检查来解决这个问题。他们在正常情况下不参与您的申请的执行。
由于这些原因,Vulkan既非常冗长又有点脆弱。你需要做大量的工作,以获得Vulkan运行良好,不正确的使用API通常会导致图形损坏,甚至程序崩溃,在旧的API,你会收到一个有用的错误消息。作为交换,Vulkan提供对设备的更多控制,一个干净的线程模型,以及比它取代的API更高的性能。
此外,Vulkan被设计为不仅仅是一个图形API。它可以用于异构设备,如图形处理单元(GPU),数字信号处理器(DSP)和固定功能硬件。功能分为粗粒度,宽泛重叠的类别。当前版本的Vulkan定义了传输类别,用于复制数据;计算类别,用于在计算工作负载上运行着色器;和图形类别,其中包括光栅化,图元装配,混合,深度和模板测试,以及图形编程人员熟悉的其他功能。
只要对每个类别的支持是可选的,就可能有一个不支持图形的Vulkan设备。因此,即使用于将图片放置到显示设备(其被称为呈现)上的API不仅是可选的,而且被提供作为对Vulkan的扩展而不是作为核心API的一部分。
实例,设备和队列
Vulkan包括一个层次结构的功能,从顶层开始与实例,将所有支持Vulkan的设备集合在一起。每个设备然后暴露一个或多个队列。正是这些队列执行处理应用程序所有的请求。
Vulkan实例是一个软件构造,在逻辑上将应用程序的状态与在应用程序上下文中运行的其他应用程序或库分离。系统中的物理设备被呈现为实例的成员,每个实体具有某些能力,包括可用队列的选择。
物理设备通常表示单个硬件或互连的硬件集合。在任何系统中都有固定的有限数量的物理设备,除非系统支持重新配置,例如(hot-plug)热插拔。由实例创建的逻辑设备是围绕物理设备的软件结构,并且表示与特定物理设备相关联的资源的预留。这包括物理设备上可用队列的可能子集。可以创建表示单个物理设备的多个逻辑设备,并且它是您的应用程序将花费大部分时间进行交互的逻辑设备。
图1.1说明了这种层次结构。在图中,应用程序创建了两个Vulkan实例。系统中有三个物理设备可供这两个实例使用。枚举之后,应用程序在第一个物理设备上创建一个逻辑设备,为第二个设备创建两个逻辑设备,为第三个创建另一个逻辑设备。每个逻辑设备启用其对应的物理设备队列的不同子集。在实践中,大多数Vulkan应用程序将不会是这么复杂,并将使用单个实例简单地为系统中的一个物理设备创建单个逻辑设备。图1.1仅用于展示Vulkan系统的灵活性。
以下小节讨论如何创建Vulkan实例,查询系统中的物理设备,附加与其中一个对应的逻辑设备,最后检索设备公开的队列的句柄。
Vulkan实例
Vulkan可以看作是你的应用程序的子系统。 一旦你的应用程序链接到Vulkan库并初始化它,它跟踪一些状态。 因为Vulkan不会在您的应用程序中引入任何全局状态,所有跟踪状态必须存储在您提供的对象中。 这是实例对象,由VkInstance对象表示。 要构造一个,我们将调用我们的第一个Vulkan函数vkCreateInstance(),其原型是
点击这里查看代码图像
VkResult vkCreateInstance (
const VkInstanceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkInstance* pInstance);
这个声明是典型的Vulkan函数。 当超过少数的参数传递给Vulkan时,函数通常会获取指向这个结构的指针。 这里,pCreateInfo是指向的一个指针包含描述新Vulkan实例的参数的VkInstanceCreateInfo结构的实例。 VkInstanceCreateInfo的定义是
typedef struct VkInstanceCreateInfo {
VkStructureType sType;
const void* pNext;
VkInstanceCreateFlags flags;
const VkApplicationInfo* pApplicationInfo;
uint32_t enabledLayerCount;
const char* const* ppEnabledLayerNames;
uint32_t enabledExtensionCount;
const char* const* ppEnabledExtensionNames;
} VkInstanceCreateInfo;
几乎每个Vulkan结构中用于向API传递参数的第一个成员是sType字段,它告诉Vulkan这是什么类型的结构。核心API和任何扩展中的每个结构都有一个分配的结构标签。通过检查此标记,Vulkan工具,图层和驱动程序可以确定结构的类型,以便进行验证和在扩展中使用。此外,pNext字段允许将链接的结构列表传递给函数。这允许扩展参数集合,而不需要在扩展中批量替换核心结构。因为我们在这里使用核心实例创建结构,我们在sType字段中传递VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,并简单地将pNext设置为nullptr。
VkInstanceCreateInfo的flags字段保留供将来使用,并应设置为零。下一个字段pApplicationInfo是描述应用程序的另一个结构的可选指针。你可以将它设置为nullptr,但一个行为良好的应用程序应该填充一些有用的东西。 pApplicationInfo指VkApplicationInfo结构的一个实例,其定义是
typedef struct VkApplicationInfo {
VkStructureType sType;
const void* pNext;
const char* pApplicationName;
uint32_t applicationVersion;
const char* pEngineName;
uint32_t engineVersion;
uint32_t apiVersion;
} VkApplicationInfo;
再次,我们看到此结构中的sType和pNext字段。 sType应设置为
VK_STRUCTURE_TYPE_APPLICATION_INFO,我们可以将pNext留作nullptr。 pApplicationName是一个指向nul-terminated(1)字符串的指针,该字符串包含您的名称应用程序,applicationVersion是应用程序的版本。 这允许工具和驱动程序做出关于如何处理您的应用程序的决定,而无需猜测(2)哪个应用程序正在运行。 同样,pEngineName和engineVersion分别包含应用程序所基于的引擎或中间件的名称和版本。
(1)nul,其字面值为零的ASCII字符正式称为NUL。 现在,停止告诉我把它改为NULL。 这是一个指针,而不是一个字符的名称。
(2)每个优秀的应用程序相比较都有自己好的独特的地方。 同时,应用程序由人类编写,人类编写的代码总有错误。 为了完全优化或解决应用程序错误,驱动程序有时会使用可执行文件名称甚至应用程序行为来猜测运行的应用程序并适当地改变行为。 即使不理想,这种新机制至少消除了猜测部分的工作。
最后,apiVersion包含您的应用程序期望运行的Vulkan API的版本。这应该设置为您的应用程序运行所需的Vulkan的绝对最小版本 - 不仅仅是您刚刚安装的头的版本。这允许最广泛的设备和平台的分类来运行您的应用程序,即使他们的Vulkan实现的更新可能不可用。
返回到VkInstanceCreateInfo结构,我们看到enabledLayerCount和ppEnabledLayerNames字段。这些是分别要启用的实例层数及其名称的个数。图层用于拦截Vulkan API,并提供日志记录,概要分析,调试或其他附加功能。如果不需要图层,只需将enabledLayerCount设置为零,并将ppEnabledLayerNames保留为nullptr。就像enabledExtensionCount是要启用的扩展数的计数(3),ppEnabledExtensionNames则是其名称对应的列表。再次说明,如果我们不使用任何扩展,我们可以分别设置这些字段为零和nullptr。
(3)与OpenGL一样,Vulkan支持扩展作为API的中心部分。 但是,在OpenGL中,我们将创建一个上下文,查询支持的扩展,然后开始使用它们。 这意味着驱动程序需要假设您的应用程序可能会在任意时刻开始使用扩展程序而时刻做出准备。 此外,它不能告诉你正在寻找哪个扩展,这使该过程更加困难。 在Vulkan中,应用程序需要选择启用扩展并明确启用它们。 这允许驱动程序禁用未使用的扩展,并且使应用程序难以使用作为其无意启用的扩展的一部分的功能意外启动。
最后,返回到vkCreateInstance()函数,pAllocator参数是指向您的应用程序提供的主机内存分配器的指针,以便管理Vulkan系统使用的主机内存。 将此设置为nullptr会导致Vulkan系统使用自己的内部分配器,这是我们将会在这里这样做。 应用程序管理的主机内存将在第2章“内存和资源”中介绍。
假设vkCreateInstance()函数成功,它将返回VK_SUCCESS并在pInstance参数指向的变量中为新实例放置一个句柄。 句柄是引用对象的值。 Vulkan句柄总是64位宽,不管主机系统的位数。 一旦我们有一个处理我们的Vulkan实例,我们可以使用它来调用其他实例函数。
Vulkan物理设备
一旦我们有一个实例,我们可以使用它来发现系统中安装的Vulkan兼容设备。 Vulkan有两种类型的设备:物理和逻辑。 物理设备通常是系统的一部分 - 图形卡,加速器,DSP或其他组件。 在系统中有固定数量的物理设备,并且每个具有固定的能力集合。 逻辑设备是物理设备的软件抽象,以应用程序指定的方式配置。 逻辑设备是您的应用程序将花费大部分时间处理的逻辑设备,但在我们可以创建逻辑设备之前,我们必须发现连接的物理设备。 为此,我们调用vkEnumeratePhysicalDevices()函数,其原型是
VkResult vkEnumeratePhysicalDevices (
VkInstance instance,
uint32_t* pPhysicalDeviceCount,
VkPhysicalDevice* pPhysicalDevices);
vkEnumeratePhysicalDevices()函数的第一个实例参数是我们之前创建的实例。接下来,pPhysicalDeviceCount参数是指向无符号整数变量的指针,该参数既是输入也是输出。作为输出,Vulkan将系统中的物理设备数量写入其中。作为输入,应该使用应用程序可以处理的最大数量的设备进行预初始化。 pPhysicalDevices参数是指向此数目的VkPhysicalDevice句柄的数组的指针。
如果你只想知道系统中有多少设备,将pPhysicalDevices设置为nullptr,Vulkan将忽略pPhysicalDeviceCount的初始值,使用支持的设备数量覆盖它。您可以通过调用vkEnumeratePhysicalDevices()两次动态调整VkPhysicalDevice数组的大小,第一次只需要将pPhysicalDevices设置为nullptr(尽管pPhysicalDeviceCount仍然必须是有效的指针),并且第二次将pPhysicalDevices设置为数组,该数组大小是第一请求的物理设备的数量。假设没有问题,vkEnumeratePhysicalDevices()返回VK_SUCCESS并将pPhysicalDeviceCount中识别的物理设备数量及其句柄存储在pPhysicalDevices中。清单1.1展示了一个构造VkApplicationInfo和VkInstanceCreateInfo结构的示例,创建Vulkan实例,查询它支持的设备数量,最后查询物理设备处理它们自己。这是示例框架中的vkapp :: init的略微简化的版本。
代码1.1:创建Vulkan实例
VkResult vkapp::init()
{
VkResult result = VK_SUCCESS;
VkApplicationInfo appInfo = { };
VkInstanceCreateInfo instanceCreateInfo = { };
// A generic application info structure
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "Application";
appInfo.applicationVersion = 1;
appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0);
// Create the instance.
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceCreateInfo.pApplicationInfo = &appInfo;
result = vkCreateInstance(
&instanceCreateInfo, nullptr, &m_instance);
if (result == VK_SUCCESS)
{
/*First figure out how many devices are in the system.*/
uint32_t physicalDeviceCount = 0;
vkEnumeratePhysicalDevices(m_instance, &physicalDeviceCount,nullptr);
if (result == VK_SUCCESS)
{
/* Size the device array appropriately and get the physical device handles. */ m_physicalDevices.resize(physicalDeviceCount);
vkEnumeratePhysicalDevices(m_instance, &physicalDeviceCount,&m_physicalDevices[0]);
}// if
} // if
return result;
}
物理设备句柄用于查询设备的功能,并最终创建逻辑设 备。 我们执行的第一个查询是vkGetPhysicalDeviceProperties(),它填充描述物理设备的所有属性的结构。 它的原型是
void vkGetPhysicalDeviceProperties (
VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties);
当调用vkGetPhysicalDeviceProperties()时,传递在physicalDevice参数中从vkEnumeratePhysicalDevices()返回的一个句柄,并在pProperties中传递一个指向VkPhysicalDeviceProperties结构的实例的指针。 这是一个包含大量描述物理设备属性的字段的大的数据结构。 它的定义是
typedef struct VkPhysicalDeviceProperties {
uint32_t apiVersion;
uint32_t driverVersion;
uint32_t vendorID;
uint32_t deviceID;
VkPhysicalDeviceType deviceType;
char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];
uint8_t pipelineCacheUUID[VK_UUID_SIZE];
VkPhysicalDeviceLimits limits;
VkPhysicalDeviceSparseProperties sparseProperties;
} VkPhysicalDeviceProperties;
apiVersion字段包含设备支持的最高版本的Vulkan,而driverVersion字段包含用于控制设备的驱动程序的版本。由供应商提供,因此比较供应商的驱动程序版本没有意义。 vendorID和deviceID字段标识供应商和设备,并且通常是PCI供应商和设备标识符(4)。
(4)没有官方的PCI供应商或设备标识符的中央存储库。 PCI SIG(http://pcisig.com/)为其成员分配供应商标识符,这些成员为其产品分配设备标识符。 一个相当全面的人类和机器都可读形式的列表可以从http://pcidatabase.com/获得。
deviceName字段将包含一个用户可读的字符串命名设备。 pipelineCacheUUID字段用于管道缓存,我们将在第6章“着色器和流水线”中介绍。
除了刚刚列出的属性,VkPhysicalDeviceProperties结构嵌入VkPhysicalDeviceLimits和VkPhysicalDeviceSparseProperties,它们包含物理设备的最小和最大限制以及与稀疏纹理相关的属性。这些结构中有很多信息,我们将单独讨论这些字段,因为讨论了相关特性,而不在这里列举。
除了核心特征之外,其中一些具有可选的较高限制或界限,Vulkan具有可由物理设备支持的多个可选特征。如果设备宣传对某项功能的支持,则该设备必须仍然处于启用状态(非常类似于扩展程序),但一旦启用,该功能就像任何核心功能一样成为API的头等公民。要确定物理设备支持哪些功能,请调用vkGetPhysicalDeviceFeatures(),其原型是
void vkGetPhysicalDeviceFeatures (
VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures* pFeatures);
同样,VkPhysicalDeviceFeatures结构非常大,并且为Vulkan支持的每个可选功能都有一个布尔字段。 这里有太多要单独列出和描述的字段,但是本章末尾提供的示例应用程序会读取功能集并打印其内容。
物理设备内存
请点击链接:
第一章(2)物理内存