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 会抛出错误,你可以及时调整代码或提高要求。
五、核心优化要点总结
-
按需请求,避免“全量申请”
只申请你的应用真正依赖的特性与限制。申请所有能力会掩盖开发期的越界问题,导致在用户设备上出现难以排查的故障。 -
先检测
adapter.limits和adapter.features,再决定申请策略
不要直接假设设备支持某项能力。在调用requestDevice前先检查适配器,若关键能力缺失可以提前降级或提示用户,而不是让requestDevice直接 reject。 -
让错误尽早暴露
在开发阶段只申请必要的最小权限。这样一旦代码超出限制或使用了未申请的特性,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-astc | ASTC 纹理压缩 | 移动端常用格式,对应 OpenGL 扩展 GL_KHR_texture_compression_astc_ldr |
| texture-compression-bc | BC 纹理压缩 | 桌面端常用格式,对应 OpenGL 扩展 GL_EXT_texture_compression_s3tc 等 |
| float32-filterable | 可过滤 float32 | 允许 rgba32float 纹理使用线性过滤,默认只支持最近邻 |
| minFilter | 缩小过滤器 | 纹理在缩小采样时的过滤方式,可选 'linear' 或 'nearest' |
| bgra8unorm-storage | BGRA8 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。
