WebGPU 可选特性与限制(WebGPU Optional Features and Limits)

原文作者:webgpufundamentals Contributors
原文链接:https://webgpufundamentals.org/webgpu/lessons/webgpu-limits-and-features.html
本文为深度解读,并非逐字全盘翻译。重点在于提炼 WebGPU 可选特性(optional features)与限制(limits)的申请机制、最佳实践以及相关术语的准确理解。


一、WebGPU 的两张“能力清单”

与 WebGL 不同,WebGPU 将硬件能力抽象为一组限制(limits) 和可选特性(features)。每个物理适配器(adapter)都会暴露自己的 limits 对象和 features 数组,开发者可以在申请设备(device)时按需请求。

当你通过 navigator.gpu.requestAdapter() 获取适配器后:

const adapter = await navigator.gpu?.requestAdapter();
console.log(adapter.limits.maxColorAttachments);

可能打印出 8,表示该适配器最多支持 8 个颜色附件(color attachments)。

adapter.features 则返回一个字符串数组,例如:

const adapter = await navigator.gpu?.requestAdapter();
console.log(adapter.features);

可能输出 ["texture-compression-astc", "texture-compression-bc"],表示这些纹理压缩格式可以申请使用。

所有设备都保证满足一组最小限制(minimum limits)。如果你的应用运行在最小限制以内,并且不依赖任何可选特性,那么理论上可以在所有支持 WebGPU 的设备上运行。


二、申请限制与特性

默认情况下,requestDevice() 返回的设备只拥有最小限制,并且不启用任何可选特性。如果你需要更高的上限或特定功能,必须在调用 requestDevice 时通过 requiredLimits 和 requiredFeatures 显式声明。

例如,申请 1 GiB 的 maxBufferSize 以及可过滤 float32 纹理特性:

const k1Gig = 1024 * 1024 * 1024;
const adapter = await navigator.gpu?.requestAdapter();
const device = await adapter?.requestDevice({
  requiredLimits: { maxBufferSize: k1Gig },
  requiredFeatures: [ 'float32-filterable' ],
});

'float32-filterable' 特性允许 'rgba32float' 纹理使用线性过滤(例如 minFilter: 'linear'),而默认情况下该格式只能使用最近邻过滤('nearest')。如果任一请求无法满足,requestDevice 会 reject,你需要处理失败情况。


三、反模式:不要“一口气全要”

有些开发者可能会想:把适配器的所有 limits 和 features 全部申请下来,然后检测自己需要的那些是否可用。这种代码看起来简洁,实则很危险:

function objLikeToObj(src) {
  const dst = {};
  for (const key in src) {
    dst[key] = src[key];
  }
  return dst;
}

//
// BAD!!! ?
//
async function main() {
  const adapter = await navigator?.gpu.requestAdapter();
  const device = await adapter?.requestDevice({
    requiredLimits: objLikeToObj(adapter.limits),
    requiredFeatures: adapter.features,
  });
  if (!device) {
    fail('need webgpu');
    return;
  }

  const canUse128KUniformsBuffers = device.limits.maxUniformBufferBindingSize >= 128 * 1024;
  const canStoreToBGRA8Unorm = device.features.has('bgra8unorm-storage');
  const canIndirectFirstInstance = device.features.has('indirect-first-instance');
}

问题在于:在开发机上你可能“意外地”超出了限制或使用了某个特性,但因为申请了所有能力,一切都能正常运行。当用户手机缺少某个特性或某限制更低时,程序会神秘崩溃,而你完全没有意识到自己触碰了可选边界。


四、推荐方式:先检测,再按需申请

正确做法是先检测适配器是否满足你的核心需求,然后只申请那些你确定需要的特性与限制。这样一旦你的代码无意中超出限制或使用了未申请的特性,WebGPU 会立即报错,让你能在开发期发现。

修正后的示例(已修复原文代码中的几处小笔误):

const adapter = await navigator?.gpu.requestAdapter();

const canUse128KUniformsBuffers = adapter?.limits.maxUniformBufferBindingSize >= 128 * 1024;
const canStoreToBGRA8Unorm = adapter?.features.has('bgra8unorm-storage');
const canIndirectFirstInstance = adapter?.features.has('indirect-first-instance');

// 如果某项绝对必须的能力缺失,立即失败
if (!canUse128KUniformsBuffers) {
  alert('抱歉,您的设备可能过旧或性能不足');
  return;
}

// 只申请真正需要的特性和限制
const device = await adapter?.requestDevice({
  requiredFeatures: [
    ...(canStoreToBGRA8Unorm ? ['bgra8unorm-storage'] : []),
    ...(canIndirectFirstInstance ? ['indirect-first-instance'] : []),
  ],
  requiredLimits: {
    maxUniformBufferBindingSize: 128 * 1024,
  },
});

这样,如果你后来不小心创建了一个超过 128 KiB 的 uniform buffer,或者用了未申请的特性,WebGPU 会抛出错误,你可以及时调整代码或提高要求。


五、核心优化要点总结

  1. 按需请求,避免“全量申请”
    只申请你的应用真正依赖的特性与限制。申请所有能力会掩盖开发期的越界问题,导致在用户设备上出现难以排查的故障。

  2. 先检测 adapter.limits 和 adapter.features,再决定申请策略
    不要直接假设设备支持某项能力。在调用 requestDevice 前先检查适配器,若关键能力缺失可以提前降级或提示用户,而不是让 requestDevice 直接 reject。

  3. 让错误尽早暴露
    在开发阶段只申请必要的最小权限。这样一旦代码超出限制或使用了未申请的特性,WebGPU 会立即报错。这比生产环境中依赖“恰好可用”更稳健,也更容易维护跨设备兼容性。


六、GLSL / WebGL 专业术语检查

本文涉及 WebGPU 而非 GLSL 着色器代码,但包含大量与 WebGL / 图形 API 相关的术语。以下是对比与解释:

英文术语中文推荐翻译说明
adapter适配器GPU 物理设备的抽象,类似 WebGL 的 WEBGL_debug_renderer_info 查询结果
device设备逻辑 GPU 上下文,类似 WebGL 的 WebGLRenderingContext
limits限制硬件能力上限集合,如最大纹理尺寸、最大 uniform 缓冲区大小等
features特性 / 功能可选 GPU 能力,如浮点纹理过滤、压缩纹理格式等,对应 WebGL 扩展
requestDevice请求设备创建逻辑设备,类似 WebGL 的 getContext(),但显式声明所需能力
requiredLimits必需限制申请设备时要求的最小限制集合
requiredFeatures必需特性申请设备时要求启用的可选特性列表
maxColorAttachments最大颜色附件数类似 OpenGL 的 GL_MAX_COLOR_ATTACHMENTS
maxBufferSize最大缓冲区大小类似 OpenGL 的 GL_MAX_SSBO_SIZE(但此处为通用 buffer)
maxUniformBufferBindingSize最大统一缓冲区绑定大小类似 OpenGL 的 GL_MAX_UNIFORM_BLOCK_SIZE
texture-compression-astcASTC 纹理压缩移动端常用格式,对应 OpenGL 扩展 GL_KHR_texture_compression_astc_ldr
texture-compression-bcBC 纹理压缩桌面端常用格式,对应 OpenGL 扩展 GL_EXT_texture_compression_s3tc 等
float32-filterable可过滤 float32允许 rgba32float 纹理使用线性过滤,默认只支持最近邻
minFilter缩小过滤器纹理在缩小采样时的过滤方式,可选 'linear' 或 'nearest'
bgra8unorm-storageBGRA8 UNORM 存储允许 bgra8unorm 纹理用于存储纹理(storage texture)操作
indirect-first-instance间接首实例允许间接绘制/计算时指定首实例索引,对应 OpenGL 的 GL_ARB_indirect_parameters 部分能力
record<DOMString, GPUSize64>WebIDL 记录类型规范中 requiredLimits 的类型定义,要求普通对象转换为记录时只复制自有属性

上述术语在 WebGPU 环境下与 GLSL / WebGL 概念有较强对应关系,理解这些映射有助于从 WebGL 迁移至 WebGPU。本文不直接涉及 GLSL 着色器代码,但涉及的 uniform buffer、storage buffer、纹理过滤等概念与 GLSL 编程紧密相连。


结语

WebGPU 的可选特性与限制机制比 WebGL 的扩展系统更加系统和显式。合理利用 adapter.limits 和 adapter.features,在 requestDevice 时只申请必要的权限,是保证应用跨设备稳定运行的关键。希望这篇深度解读能帮助你更好地驾驭 WebGPU 的能力边界。

查看系统所有适配器的特性与限制,可访问 webgpureport.org。