在 Vulkan 的设计哲学中,为了达到对硬件的绝对掌控,它把“显卡”这个概念极其显式地拆分成了两个层面:物理设备(Physical Device) 和 逻辑设备(Logical Device)。

简单来说:

  • 物理设备 代表的是你电脑里那块看得见、摸得着的整块 GPU 显卡芯片(硬件实体)。

  • 逻辑设备 代表的是你的应用程序与这块显卡之间建立的软件通信通道/会话(软件接口)。

1. 物理设备 (VkPhysicalDevice)#

物理设备 是对系统当前实际插在主板上的硬件 GPU 的抽象。

核心特点#

  • 只读的硬件底牌: 你不能“创建”一个物理设备,你只能去“枚举(Enumerate)”和“查询(Query)”它。它就像是显卡厂商直接写死在硬件和驱动里的“说明书”。

  • 区分不同的显卡: 如果你的电脑(比如游戏笔记本)同时有 Intel 核显和 NVIDIA 独显,Vulkan 就会枚举出两个不同的 VkPhysicalDevice 句柄。

能够查询到什么?#

通过 Vulkan 提供的查询函数(如 vkGetPhysicalDeviceProperties 和 vkGetPhysicalDeviceFeatures),你可以把这块硬件的底细摸得一清二楚:

  1. 基础属性 (Properties): 显卡名称(例如 RTX 5070 Ti)、厂商 ID、驱动版本、以及它是独立显卡(Discrete)还是集成显卡(Integrated)。

  2. 硬件特性 (Features): 这块显卡支不支持硬件光线追踪?支不支持网格着色器(Mesh Shader)?支不支持几何着色器?

  3. 内存信息 (Memory Properties): 这张卡有多少显存(Device Local Memory)?有多少可以直接映射的系统内存?

  4. 队列族 (Queue Families): 之前聊过的,它有哪些车间?支持图形、计算还是传输?

2. 逻辑设备 (VkDevice)#

逻辑设备 是你在选中某块物理设备后,向它正式申请并创建出来的虚拟操作接口。

核心特点#

  • 真正干活的句柄: 绝大多数 Vulkan 的核心操作(创建缓冲区、创建纹理、创建渲染管线、创建着色器模块等),其函数的第一个参数全都是 VkDevice。

  • 按需定制: 创建逻辑设备时,你是在对物理设备说:“我决定用你了,但我不需要你的全部功能,我只要开启你的光追特性,并且帮我从图形队列族里实例化出一个队列来。”

  • 多实例并行: 针对同一个物理设备,你可以创建多个独立的逻辑设备。它们在同一个程序内互不干扰。

3. 两者的关系与生命周期#

物理设备和逻辑设备的关系,非常符合现代 C++ 的资源管理逻辑:

  1. 多对一的选择: 系统中可以有多个物理设备,但你的程序通过筛选算法,最终通常只会选择其中一个最合适的物理设备。

  2. 一对多的实例化: 选定物理设备后,基于它创建出一个或多个逻辑设备。

  3. 生命周期分离:

    • VkPhysicalDevice 的生命周期跟随 Vulkan 实例(VkInstance),不需要也不能手动销毁。

    • VkDevice 必须遵循 RAII 原则。在程序退出或切换显卡时,你必须显式调用 vkDestroyDevice(device, nullptr) 来释放它以及挂在它底下的所有资源。

总结#

  • 物理设备(Physical) 回答的是 “硬件能做到什么”(Capabilities)。

  • 逻辑设备(Logical) 回答的是 “我的程序要用它做什么”(Activation)。

这种分离保证了 Vulkan 的纯净性:无论用户的电脑插了多少张卡,通过物理设备盘点清楚后,你的图形引擎主体只需要抱着创建好的逻辑设备(VkDevice)一路无脑画画就可以了。