WebGPU把GPU的硬件能力拆分为一个个Feature,并通过GPUAdapter实例的features属性对外暴露。任何高级功能,比如纹理压缩格式、存储纹理、深度钳制等,在使用之前都必须先确认适配器支持对应的FeatureName。FeatureName本身只是一种字符串枚举,但在TypeScript工程里,如果我们仅仅把它当string类型处理,就丢失了编译期检查的价值。本文从类型建模的角度出发,讨论如何用TypeScript精确表达WebGPU的硬件特性,尤其是纹理压缩格式与存储纹理格式相关的特性名与数据类型。

GPUFeatureName在TypeScript中的正确打开方式
在TypeScript的WebGPU类型定义中,GPUAdapter的features属性被声明为只读的GPUFeatureName数组。这个类型在最新的lib.dom.d.ts以及@webgpu/types库中,都已经从宽松的string收紧为字面量联合类型。也就是说,浏览器运行时返回的虽然是普通字符串,但TypeScript编译阶段已经把所有合法特性名列出来了。
采用字面量联合类型带来的最直接的好处是自动补全和拼写校验。假如把特性名错写成texture-compression-b2,编译器会立刻给出提示,而不是等到运行时报错。下面的代码展示了基本的使用方式:
import type { GPUFeatureName } from "@webgpu/types";
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
throw new Error("当前浏览器无法获得WebGPU适配器");
}
// 只读数组,类型为 GPUFeatureName[]
console.log(adapter.features);
// 字面量正好是联合类型中的成员,类型检查通过
const supportBC = adapter.features.includes("texture-compression-bc");
// 如果写 adapter.features.includes("texture-compression-b2")
// TypeScript 会在编译期直接报错
需要注意的是,includes方法的参数类型要求是GPUFeatureName。这意味着普通字符串变量不能直接传入,必须先通过类型守卫或断言收窄。这个限制恰好提醒我们:在业务代码中应该尽量使用显式声明的特性常量,而不是到处裸写字符串。
纹理压缩格式特性:BC、ETC2与ASTC
纹理压缩是移动端和桌面端性能优化的关键手段。WebGPU把最常见的三种纹理压缩能力分别定义为texture-compression-bc、texture-compression-etc2和texture-compression-astc。这三种特性对应的压缩算法、纹理格式与数据类型有明显差异,开发者需要根据目标平台选择合适的格式。
| 特性名 | 代表性格式 | 数据类型 | 适用平台 |
|---|---|---|---|
| texture-compression-bc | bc1-rgba-unorm、bc3-rgba-unorm、bc7-rgba-unorm | unorm、snorm、float | Windows、Xbox 等桌面设备 |
| texture-compression-etc2 | etc2-rgb8unorm、etc2-rgb8unorm-srgb | unorm | Android、iOS 等移动设备 |
| texture-compression-astc | astc-4x4-unorm、astc-8x8-rgba-unorm-srgb | unorm、float | 移动端与桌面端通用 |
从数据类型的角度来看,bc格式家族中还包含bc6h这种float类型的HDR压缩格式,而ETC2只支持固定的8位每像素压缩率。ASTC则允许通过块大小参数在画质与体积之间自由权衡。这些细节在TypeScript类型中无法直接体现,因此通常的做法是把特性名建模成一个独立的联合类型,再把判断逻辑收敛到工具函数中。
export const TEXTURE_COMPRESSION_FEATURES = [
"texture-compression-bc",
"texture-compression-etc2",
"texture-compression-astc",
] as const;
export type TextureCompressionFeature = typeof TEXTURE_COMPRESSION_FEATURES[number];
export function hasTextureCompressionSupport(
features: readonly string[]
): TextureCompressionFeature[] {
return TEXTURE_COMPRESSION_FEATURES.filter((feature) =>
(features as readonly string[]).includes(feature)
);
}
上面的代码利用as const把数组转换为只读元组,再通过typeof + 索引访问类型提取出联合类型。这样一来,TEXTURE_COMPRESSION_FEATURES既是运行时的判定依据,又是编译期的类型来源,做到一份定义两处使用。
存储纹理格式特性与WGSL的数据类型映射
存储纹理是WebGPU计算着色器里最灵活的读写载体,通过WGSL中的texture_storage_2d声明。常见的存储纹理特性包括bgra8unorm-storage、rgba8unorm-storage和rgba16float-storage。它们分别表示设备允许把bgra8unorm、rgba8unorm、rgba16float这类纹理格式用作可读写存储纹理。
这里有一个容易混淆的点:bgra8unorm-storage特性虽然名称里写的是bgra8unorm,但在WGSL着色器中访问该纹理时,format参数仍然写作rgba8unorm。这是因为WGSL内部统一按rgba通道顺序处理,bgra只是硬件层面的内存布局差异。理解这一点对编写正确的存储纹理代码很有帮助。
export type StorageTextureFeature =
| "bgra8unorm-storage"
| "rgba8unorm-storage"
| "rgba16float-storage";
export function resolveStorageFormat(
supportedFeatures: readonly string[],
preferred: readonly StorageTextureFeature[]
): StorageTextureFeature | null {
for (const format of preferred) {
if ((supportedFeatures as readonly string[]).includes(format)) {
return format;
}
}
return null;
}
在实际项目中,可以把用户偏好数组按画质从高到低排列,例如优先选择rgba16float-storage,其次rgba8unorm-storage,最后退回bgra8unorm-storage。resolveStorageFormat返回第一个被设备支持的特性名,如果全部不支持就返回null,上层逻辑据此走降级方案。
用satisfies与常量对象构建类型安全查询层
随着项目规模增长,散落在各处的特性判断会让代码变得难以维护。比较推荐的做法是建立一个集中管理特性名的常量对象,并利用TypeScript 4.9引入的satisfies操作符,让常量对象同时满足人类可读的键名与严格的GPUFeatureName类型检查。
import type { GPUFeatureName } from "@webgpu/types";
const WEBGPU_FEATURES = {
depthClipControl: "depth-clip-control",
textureCompressionBC: "texture-compression-bc",
textureCompressionETC2: "texture-compression-etc2",
textureCompressionASTC: "texture-compression-astc",
bgra8unormStorage: "bgra8unorm-storage",
rgba8unormStorage: "rgba8unorm-storage",
rgba16floatStorage: "rgba16float-storage",
} as const satisfies Record<string, GPUFeatureName>;
这个写法既保留了键名的自动补全,又确保了每个字符串值都必须是合法的WebGPU特性名。即使未来浏览器增加了新的特性,只要@webgpu/types没有同步更新,编译期就会提示缺少对应成员,从而避免在更新SDK时漏掉类型声明。
最后,还可以定义一个通用的类型守卫函数,把string收窄为GPUFeatureName,方便在特性查询分支中获得完整的类型支持:
export function isFeatureSupported(
features: readonly GPUFeatureName[],
feature: string
): feature is GPUFeatureName {
return (features as readonly string[]).includes(feature);
}
// 使用示例
if (isFeatureSupported(adapter.features, WEBGPU_FEATURES.textureCompressionBC)) {
// 在此分支中,TypeScript 已把字符串收窄为 GPUFeatureName
createCompressedTextureFromBC(adapter);
}
把特性查询统一收口到isFeatureSupported之后,业务代码里就不再需要关心adapter.features的具体类型,也不必频繁使用as断言。无论是纹理压缩格式判断,还是存储纹理格式的选型,都可以复用同一套类型安全机制,让WebGPU特性管理变得更加可靠。
WebGPUTypeScript纹理压缩格式修改时间:2026-08-25 08:04:40