1. Introducation

图形 API 是一套预定义的函数、数据结构、协议和工具的集合。它是连接应用程序和图形硬件的桥梁。

图形 API 规定了应用程序如何向 GPU 发出命令,以及 GPU 需要提供哪些功能。基于这份相同的协议, GPU 制造商和应用程序的开发者开发的产品就能兼容。

如果没有图形 API,那么意味着开发者不得不为每一款特定的 GPU 编写机器码,这是难以想象的。

通常,图形 API 通过管理以下内容,协同完成从数据到图像的转换:

  1. 资源

    • 资源指所有需要送入 GPU 处理的数据,包括几何数据(顶点、索引)、纹理、着色器程序等
    • 图形 API 提供接口,让应用程序在 GPU 上创建、填充、销毁这些资源,并管理它们在 GPU 内存中的分配与布局
  2. 着色器

    • 着色器是运行在GPU上的微型程序,是渲染管线的可编程核心
    • 图形 API 提供专用的着色器语言,并负责将开发者编写的着色器代码编译成 GPU 能理解的二进制格式,最终链接成一个完整的着色器程序供管线使用
  3. 渲染管线与状态机

    • 渲染管线是一个固定的、可配置的流水线,定义了从顶点数据到像素的处理步骤

    • 图形 API 允许开发者配置流水线。包括绑定着色器、设置固定功能状态(如深度测试、混合模式)、绑定资源(如纹理)等。这些配置构成了一个状态机,现代图形 API 往往要求开发者将大部分管线状态预先打包成一个管线状态对象,以提升效率

      广义状态机:其后续输出依赖于之前设置的上下文(状态),都可以被视为状态机。

      传统图形 API 如 OpenGL 等是一种单一的、全局的、隐式管理的巨型状态机,易用但危险,开销较大。

      而现代图形 API 如 Vulkan 将状态打散、显式化,分离成多个离散的、可预编译的状态对象(如 VkPipeline

  4. 命令提交与同步

    • 命名提交是 CPU 向 GPU 发送绘制指令的过程,由于 CPU 和 GPU 是独立、异步工作的,必须妥善处理它们之间的协同。
    • 图形 API 主要提供两种机制处理命令提交与同步:
      • 命令缓冲区:让 GPU 提前将一系列绘制命令录制到缓冲区中(可在多个线程上并发录制),然后高效地一次性提交给 GPU 执行
      • 同步对象:提供栅栏、信号量等工具,确保 GPU 不会在 CPU 完成数据写入前就开始读取,或者不会在上一帧完成渲染前就开始下一帧的渲染,防止数据竞争和视觉错误

传统的图形 API 如 OpenGL,DirectX11 等秉承高抽象、易用的设计哲学,在减轻开发者负担的同时也带来了不可预测的性能开销。

而现代图形 API 如 Vulkan,DirectX12,Metal,WebGPU 等要求开发者必须显式声明和管理一切,使得 CPU 的开销变得可预测。

 

2. Physical Device

在现代图形 API 中,Physical Device(物理设备)指的是图形处理器硬件——显卡。

传统图形 API 高度抽象化,由驱动程序管理底层细节(内存,同步,多 GPU 等)。而现代图形 API 将控制权交还给开发者,为了实现这一点,首先需要暴露硬件本身,Physical Device 就是这个这个暴露的接口,开发者可以查询物理设备的信息,如支持特性、资源限制等。

当查询一个 Physical Device 时,主要获取以下几类关键信息:

  1. 属性与标识:

    • 设备名称(如 “NVIDIA GeForce RTX 4080”)、供应商ID(Vendor ID)、设备ID
    • API 版本、驱动程序版本
    • 设备类型:集成显卡(INTEGRATED_GPU)、独立显卡(DISCRETE_GPU)、虚拟GPU还是CPU
  2. 功能特性

    • 图形管线特性:支持哪些着色器阶段、顶点/实现属性上限等
    • 计算能力:支持的计算着色器工作组大小等
    • 可选功能:API 核心规范只保证一个最低标准,所有的高级功能(如几何着色器,曲面细分,条件渲染,片段存储读写,光线追踪,网格着色器)都以特性或者拓展的形式给出,开发者需要显式查询并启用特定的功能
    • 纹理/缓冲区格式支持:哪些像素格式可用于渲染、采样或存储
  3. 队列家族(Queue Families)

    • 队列家族揭示了 GPU 内部的并行执行架构,一个物理设备包含多个队列家族,每个家族专精于不同类型的工作负载(图形绘制,通用计算,异步数据传输,视频编解码)
    • 查询结果包括:
      • 每个家族支持的操作类型(GRAPHICS, COMPUTE, TRANSFER, SPARSE_BINDING等)
      • 每个家族可用的队列数量,这决定了可并行提交的任务流数量
      • 每个家族是否支持与特定的显示表面进行呈现操作(Presentation)
    • 正确的队列选择是并行渲染、计算与传输重叠的关键
  4. 内存属性

    物理设备将内存组织为内存堆和内存类型:

    • 内存堆:代表物理内存池(如独立的显存、共享的系统内存)。查询会提供每个堆的大小和标志

    • 内存类型:存在于堆中,是具有特定属性组合的逻辑划分,如:

      • DEVICE_LOCAL_BIT:设备本地,访问速度最快,适合纹理和顶点缓冲区

      • HOST_VISIBLE_BIT:主机(CPU)可见,适合频繁更新的动态数据。

      • HOST_COHERENT_BIT:CPU与GPU缓存自动保持一致,简化操作但可能牺牲性能。

      • HOST_CACHED_BIT:主机端缓存,适合CPU随机读取的数据

    • 根据资源访问模式选择正确的内存类型(如将常变数据放在HOST_VISIBLE内存中,将静态纹理放在DEVICE_LOCAL内存中)是应遵循的性能优化原则

  5. 格式支持

    查询特定纹理或缓冲区格式,对于给定的像素格式(如 VK_FORMAT_R8G8B8A8_UNORM)检查其支持的特性:

    • SAMPLED_IMAGE:能否在着色器中采样。

    • STORAGE_IMAGE:能否在计算着色器中作为存储图像读写。

    • COLOR_ATTACHMENT:能否作为渲染目标。

    • BLEND:在作为渲染目标时是否支持混合

  6. 交换链支持

    此支持并非核心 API 的一部分,而是通过扩展实现。

    • 确认设备支持所需的窗口系统集成拓展(如 VK_KHR_swapchain
    • 查询设备与特定窗口表面的兼容性,包括
      • 支持的表面格式(像素格式,色彩空间)
      • 可用的呈现模式(立即模式、垂直同步 FIFO、无撕裂的 MAILBOX 等)
      • 表面能力(最小/最大图像数量、当前交换链图像分辨率范围等)
  7. 高级架构特性

    • 多GPU支持:设备组(device groups)和链接设备(linked devices)的能力,用于协同多个GPU
    • 管线缓存:通过 pipelineCacheUUID 实现跨进程的管线缓存,加速管线创建
    • 稀疏内存:支持稀疏纹理和缓冲区,允许部分 residency 和动态映射,适用于流式加载大型纹理
    • 保守光栅化(Conservative Rasterization)、光栅顺序视图(Rasterization Order View)等高级渲染特性
    • 子组(Subgroup)操作:支持Wave操作,对计算着色器优化至关重要
    • 性能查询:支持时间戳和性能查询,用于性能分析

 

3. Logical Device