低级图形API(例如Vulkan,DirectX,WebGPU和Metal)正在收敛到类似于当前GPU构建方式的模型。 图形处理单元(GPU)是异步计算单元,可以处理大量数据,例如复杂的网格几何体,图像纹理,输出帧缓冲区,变换矩阵或您要计算的任何数据。

GPU并不总是这样,最初它们是一组基于固定硬件的功能,几乎没有可编程性。 随着应用程序突破了这些非可编程系统的极限,这种情况发生了变化,这保证了GPU制造商与应用程序开发人员之间的竞争不断地推动着其设计极限。 帧缓冲区和光栅化器[Fatahalian 2018]导致了可编程的着色器,通用GPU(GPGPU)计算以及最近添加的用于光线遍历加速和AI的张量处理的硬件。 图形API与这些更改一起发展,同时增加了固定图形管线,计算着色器以及最近的射线遍历功能(DirectX 12和Vulkan Ray Tracing)。
让我们看一下图形API之间的一些异同。 我们将介绍用于以下方面的C ++ API:

  • ???? Vulkan

  • ❎ DirectX 12.x

  • ✖️ DirectX 11.x

  • ???? Metal

  • ????️ WebGPU

  • ⚪ OpenGL

OpenGL的设计起源于计算机图形学的早期,并被设计为状态机,因此其接口与现代图形API截然不同。 DirectX 11虽然比OpenGL更接近现代GPU架构,但通过delegating tasks Vulkan,DirectX 12和Metal当前让开发人员负责上下文接口的任务来使开发人员更容易。[Russell 2014]

了解现代图形API的遗留之处非常有用,因此在相关的地方将其提及。

执行顺序

无论API如何,图形应用程序通常都遵循以下执行顺序:

  1. 初始化API-创建访问API内部工作所需的核心数据结构。
  2. 加载资产-创建加载诸如着色器,描述图形管道,创建并填充供GPU执行的命令缓冲区以及将资源发送到GPU专用内存的命令所需的数据结构。
  3. 更新资产-在您的着色器中更新所有uniforms ,并在此处执行应用程序级逻辑。
  4. 呈现-将命令缓冲区列表发送到命令队列并呈现交换链。
  5. 重复2、3和4,直到来自应用程序的信号关闭。
  6. 销毁-等待GPU完成所有剩余工作,并销毁所有数据结构和句柄。

因此,我们将按此顺序跟踪Graphics API数据结构的创建和使用。

导入依赖项

现代图形API的比较_DirectX12

API Structure
Vulkan #include <vulkan/vulkan.hpp>
DirectX 12 #include <d3d12.h>
DirectX 11 #include <d3d11.h>
Metal #import <Metal/Metal.h>
WebGPU Requires Canary Browser with Flags
OpenGL Varies by OS

启动新应用程序时,您需要包括对外部API的所有依赖关系,而图形API也不例外。 根据API的不同,您可能还需要项目中的其他库,例如着色器编译器。
OpenGL是所有其他图形API的例外,其不同之处在于,取决于操作系统和您的个人设置,可以从不同位置进行多种导入。

着色器编译器

API Structure
Vulkan #include "glslang/Include/revision.h"
DirectX 12 #include <D3Dcompiler.h>
DirectX 11 #include <D3Dcompiler.h>
Metal #import <Metal/Metal.h>
WebGPU N/A
OpenGL void glShaderSource(...)

Vulkan要求您使用外部着色器编译器(例如glslang),该库可以包含在最终二进制文件中以在运行时编译着色器,也可以在构建时作为独立程序使用。
对于DirectX,建议您使用其名为DirectXShaderCompiler的开源库。 或者,您可以使用捆绑到库中的编译器。 如果您的解决方案中包含Visual Studio,Visual Studio还支持将HLSL着色器编译为二进制。
Metal着色器可以在运行时进行编译,也可以在构建时使用MacOS路径中包含的metallib命令行工具进行编译。
OpenGL不需要外部库来编译着色器,因为它包含在该库中,尽管它还支持OpenGL 4.6中的glslang。

初始化API

入口点

现代图形API的比较_图像API_02

API Structure
Vulkan vk::Instance
DirectX 12 IDXGIFactory4
DirectX 11 IDXGIFactory
Metal CAMetalLayer
WebGPU GPU
OpenGL Varies by OS

图形API的入口点通常允许您访问API的内部类。
Vulkan的入口点涉及选择要使用的API版本以及所需的任何扩展或层,例如错误检查,窗口表面等。
DirectX 11和12要求您创建一个Factory以及一个Debug数据结构(可选)。
Metal上,需要一个NSWindow带有一个带有CAMetalLayer的NSView(它是QuartzCore的一部分)。 一旦图层存在并连接到窗口,该窗口就可以使用其余的Metal API。
对于OpenGL,最接近入口点的是创建OS窗口后可以请求的特定于操作系统的上下文。

物理设备

现代图形API的比较_DirectX12_03

API Structure
Vulkan vk::PhysicalDevice
DirectX 12 IDXGIAdapter1
DirectX 11 IDXGIAdapter
Metal MTLDevice
WebGPU GPUAdapter
OpenGL glGetString(GL_VENDOR)

物理设备允许您查询重要的设备特定详细信息,例如内存大小和功能支持。

Metal是唯一的特别的,因为物理和逻辑设备都由同一数据结构共享。
除非使用制造商专有扩展,否则OpenGL无法查询任何设备详细信息。 尽管可以得到一些其他数据,例如驱动程序供应商名称,渲染器和OpenGL版本。

逻辑设备

现代图形API的比较_DirectX12_04

API Structure
Vulkan vk::Device
DirectX 12 ID3D12Device
DirectX 11 ID3D11Device
Metal MTLDevice
WebGPU GPUDevice
OpenGL N/A

 设备使您可以访问API的核心内部功能,例如创建图形数据结构(例如纹理,缓冲区,队列,管道等)。在所有现代图形API中,这种类型的数据结构在大多数情况下都是相同的, 它们之间几乎没有变化。 Vulkan在这里脱颖而出,是唯一提供对内存的控制的API,并且创建与内存相关的数据结构是通过设备完成的。

 队列

现代图形API的比较_图像API_05

API Structure
Vulkan vk::Queue
DirectX 12 ID3D12CommandQueue
DirectX 11 ID3D11DeviceContext
Metal MTLCommandQueue
WebGPU GPUQueue
OpenGL N/A

 队列使您可以排队等待GPU执行的任务。 GPU是异步计算设备,因此这里的想法是在控制项目何时添加到队列中的同时始终保持繁忙。
Vulkan队列要求您在创建设备之前指定设备将使用的队列。

命令池

现代图形API的比较_图像API_06

API Structure
Vulkan vk::CommandPool
DirectX 12 ID3D12CommandAllocator
DirectX 11 ID3D11DeviceContext
Metal MTLCommandQueue
WebGPU GPUDevice
OpenGL N/A

命令池是一种允许您创建命令缓冲区的数据结构。
Metal通过使队列也成为从中分配命令缓冲区的数据结构而脱颖而出。

Frame Backings

Window Surface

现代图形API的比较_DirectX12_07

API Structure
Vulkan vk::Surface
DirectX 12 ID3D11Texture2D
DirectX 11 ID3D11Texture2D
Metal CAMetalDrawable
WebGPU GPUCanvasContext
OpenGL Varies by OS

窗口表面允许您将所有绘图调用绑定到特定于OS的窗口。
在DirectX上,由于只有Windows / Xbox作为API的目标,因此最接近表面的是从交换链接收的纹理后备缓冲区。 交换链将接收您的窗口句柄,并从那里创建DirectX驱动程序内部的表面。
Metal的入口点是其窗口表面,但是每个帧都有一个Drawable,您可以获取当前的窗口纹理和任何相关数据。 这种行为更接近于交换链,但是Metal不包含交换链的显式数据结构。

交换链

现代图形API的比较_DirectX12_08

API Structure
Vulkan vk::Swapchain
DirectX 12 IDXGISwapChain3
DirectX 11 IDXGISwapChain
Metal NSView
WebGPU GPUSwapchain
OpenGL Varies by OS

Swapchain在给定窗口的不同后缓冲区之间切换,并控制渲染方面,例如刷新率和后缓冲区交换行为。
Metal和OpenGL在这里脱颖而出,因为API缺乏交换链的概念,而是将其留给OS窗口API。

帧缓冲器

现代图形API的比较_DirectX12_09

API Structure
Vulkan vk::Framebuffer
DirectX 12 IDXGISwapChain3
DirectX 11 ID3D11RenderTargetView
Metal MTLRenderPassDescriptor
WebGPU GPURenderPassDescriptor
OpenGL GLuint

帧缓冲区是在基于光栅的图形管线执行期间用作输出的输出纹理组。

初始化资源

纹理

现代图形API的比较_DirectX12_10

API Structure
Vulkan vk::Image & vk::ImageView
DirectX 12 ID3D12Resource
DirectX 11 ID3D11Texture2D
Metal MTLTexture
WebGPU GPUTexture & GPUTextureView
OpenGL GLuint

纹理是存储颜色信息的数据数组,并用作渲染的输入/输出。 Vulkan和WebGPU引入了具有给定纹理的多个视图的想法,这些视图可以以不同的编码格式或颜色空间查看该纹理。 Vulkan引入了用于图像和缓冲区的托管内存的想法,因此纹理是图像的三元组,使用时的图像视图(可以有多个),以及仅在设备中或在CPU-GPU可访问空间中的内存。
对于在Vulkan中管理内存的更传统的方式,我强烈建议使用AMD Vulkan内存分配器。

缓冲

现代图形API的比较_DirectX12_11

API Structure
Vulkan vk::Buffer & vk::BufferView
DirectX 12 ID3D12Resource
DirectX 11 ID3D11Buffer
Metal MTLBuffer
WebGPU GPUBuffer & GPUBufferView
OpenGL GLuint

缓冲区是数据数组,例如网格的位置数据,颜色数据,索引数据等。图像的相似规则适用于Vulkan和WebGPU中的缓冲区。

着色器

现代图形API的比较_DirectX12_12

API Structure
Vulkan vk::ShaderModule
DirectX 12 ID3DBlob
DirectX 11 ID3D11VertexShader or ID3D11PixelShader
Metal MTLLibrary
WebGPU GPUShaderModule
OpenGL GLuint

着色器往往是已编译的着色器(GLSL,HLSL,MSL等)代码的句柄,该代码将被送到给定的管线。

着色器绑定

现代图形API的比较_图像API_13

API Structure
Vulkan vk::PipelineLayout
DirectX 12 ID3D12RootSignature
DirectX 11 ID3D11DeviceContext::VSSetConstantBuffers(...)
Metal [MTLRenderCommandEncoder setVertexBuffer: uniformBuffer]
WebGPU GPUPipelineLayout
OpenGL GLint

大多数现代图形API都具有绑定数据结构,以帮助将统一的缓冲区和纹理连接到需要该数据的图形管道。 Metal的独特之处在于,您可以在命令编码器中将setVertexBuffer与setVertexBuffer绑定在一起,与Vulkan,DirectX 12和WebGPU相比,它可以更轻松地进行架构设计。

管道

现代图形API的比较_DirectX12_14

API Structure
Vulkan vk::Pipeline
DirectX 12 ID3D12PipelineState
DirectX 11 Various State Calls
Metal MTLRenderPipelineState
WebGPU GPURenderPipeline
OpenGL Various State Calls

管道是对执行栅格绘制调用,计算分派或光线跟踪分派时将执行的操作的总体描述。
DirectX 11和OpenGL在这里是唯一的,它们没有用于图形管线的专用对象,而是使用调用来设置执行绘图调用之间的管线状态。

命令缓冲区

现代图形API的比较_DirectX12_15

API Structure
Vulkan vk::CommandBuffer
DirectX 12 ID3D12GraphicsCommandList
DirectX 11 ID3D11DeviceContext
Metal MTLRenderCommandEncoder
WebGPU GPUCommandEncoder
OpenGL N/A

命令缓冲区是一个异步计算单元,您在其中描述GPU执行的过程,例如绘图调用,将数据从CPU-GPU可访问内存复制到GPU专用内存,并动态设置图形管线的各个方面例如当前裁剪。
以前,您需要声明希望GPU按程序执行的任务,然后执行这些任务,但是GPU本质上是异步的,因此驱动程序将负责确定何时将任务调度到GPU。

命令列表

现代图形API的比较_DirectX12_16

API Structure
Vulkan vk::SubmitInfo
DirectX 12 ID3D12CommandList[]
DirectX 11 ID3D11CommandList
Metal MTLCommandBuffer
WebGPU GPUCommandBuffer
OpenGL N/A

命令列表是成批推入GPU的命令缓冲区数组。 这样做的原因是保持GPU持续忙碌,从而减少CPU和GPU之间的失步 [Foley 2015]

栅栏(Fence)

现代图形API的比较_DirectX12_17

API Structure
Vulkan vk::Fence
DirectX 12 ID3D12Fence
DirectX 11 ID3D11Fence
Metal MTLFence
WebGPU GPUFence
OpenGL glFenceSync

围栏是用于同步CPU和GPU的对象。 可以指示CPU和GPU在栅栏处等待,以便彼此追赶。 这可用于管理资源分配和释放,从而更易于管理整体图形内存使用情况。 [Satran et al. 2018]

屏障(Barriers)

API Structure
Vulkan vkCmdPipelineBarrier
DirectX 12 ID3D12Fence
DirectX 11 ID3D11Fence
Metal MTLFence
WebGPU GPUFence
OpenGL glMemoryBarrier

在命令缓冲区内部更细化的同步形式

  • glMemoryBarrier

https://themaister.net/blog/2019/08/14/yet-another-blog-explaining-vulkan-synchronization/

信号

现代图形API的比较_DirectX12_18

API Structure
Vulkan vk::Semaphore
DirectX 12 HANDLE
DirectX 11 HANDLE
Metal dispatch_semaphore_t
WebGPU N/A
OpenGL Varies by OS

信号量是用于在操作之间引入依赖性的对象,例如在将命令缓冲区提交到设备队列之前等待,在获取交换链中的下一个图像之前。
Vulkan的独特之处在于,信号量是API的一部分,DirectX和Metal将其委托给OS调用。

空间,对齐方式

每个图形API的轴方向,NDC坐标方向,矩阵对齐,纹理对齐等等都可以有不同的默认值,在大多数情况下,这并不是什么大问题,您只需要在UV中翻转y值即可。 在您的片段着色器中。

纹理对齐

API Structure
Vulkan Bottom Left
DirectX 12 Top Left
DirectX 11 Top Left
Metal Top Left
WebGPU Bottom Left
OpenGL Bottom Left

DirectX与大多数封闭源API一样,将左上角用于像素空间坐标,而开放源代码则选择使用左下角。

结论

尽管每个API都有其细微的差别,但它们在设计上极为接近。 由library 架构师决定对所需API表面积的限制在哪里,就像Metal / WebGPU一样简洁明了,像Vulkan一样复杂。

其他资源

以下是一些框架,库和博客文章,它们以特定的顺序集中讨论图形api抽象:

博客文章

  • Andre Weissflog (@FlohOfWoe) wrote Sokol, an STB (eg. stb_image.h) style cross platform rendering library, with his blog archiving his thoughts during development.

  • Branimir Karadzic's bgfx library.

  • Dawn is a WebGPU implementation that uses DirectX 12, Metal, Vulkan, or OpenGL as a possible backend.

  • Dzmitry Malyshau's gfx, a rust abstraction library.

  • NVIDIA's GameWorks division released Falcor, a research framework with a thin abstraction over Vulkan and DirectX 12.

  • Wolfgang Engel (@wolfgangengel) and his team released The Forge, a cross platform rendering framework that supports DirectX 12, 11, Metal, or Vulkan.

有关所讨论的每个API的更多详细信息,请参见其规范页面:

此外,您可以在以下每个图形API上找到特定的帖子: