经纬志
首页 / luma.gl / 教程

WebGL2 与 WebGPU 的差异

先看结论

luma.gl 9 把两个后端收在同一套接口下,但差异不会消失,只会被推迟到运行期。 适配器、着色器语言、管线的可变性、附件格式与计算能力,是五处必须显式处理的地方。

最后验证 2026-11-03 · luma.gl 9.4 · 更新 2026-10-29 · 引用本文

切换后端只需换适配器

WebGL 用 @luma.gl/webgl 的 webgl2Adapter, WebGPU 用 @luma.gl/webgpu 的 webgpuAdapter。 type: 'best-available' 会先试 WebGPU,失败再退回 WebGL2。

import { luma } from '@luma.gl/core';
import { webgl2Adapter } from '@luma.gl/webgl';
import { webgpuAdapter } from '@luma.gl/webgpu';

const device = await luma.createDevice({
  type: 'best-available',
  adapters: [webgpuAdapter, webgl2Adapter],
  createCanvasContext: {
    canvas: document.getElementById('cv'),
    useDevicePixels: Math.min(window.devicePixelRatio, 2),
    autoResize: true
  }
});

console.log(device.type);      // 'webgpu' 或 'webgl'
console.log(device.info.gpu);  // WebGPU 下的适配器信息

UMD 版本同样可用,只是需要多加载一个 @luma.gl/webgpu 的包, 适配器从全局对象 luma.webgpuAdapter 取。 type 写成 'webgpu' 时不会再回退,浏览器不支持就直接失败。

着色器语言不同

WebGL2 只接受 GLSL ES 3.00,第一行必须是 #version 300 es, 片元输出要显式声明 out vec4。WebGPU 只接受 WGSL, 通过 source 传入,顶点与片元入口由 vertexEntryPoint 与 fragmentEntryPoint 指定。

if (device.type === 'webgl') {
  pipeline = device.createRenderPipeline({
    vs: glslVS,
    fs: glslFS,
    topology: 'triangle-list',
    bufferLayout: [{ name: 'positions', stepMode: 'vertex', format: 'float32x3' }]
  });
} else {
  pipeline = device.createRenderPipeline({
    source: wgslSource,
    vertexEntryPoint: 'vsMain',
    fragmentEntryPoint: 'fsMain',
    topology: 'triangle-list',
    bufferLayout: [{ name: 'positions', stepMode: 'vertex', format: 'float32x3' }],
    colorAttachmentFormats: ['rgba8unorm'],
    depthStencilAttachmentFormat: device.preferredDepthFormat
  });
}

想在两个后端共用一份着色器,用 shadertools 的模块机制写 GLSL 与 WGSL 两套片段, 由组装器按后端选择,而不是在业务代码里散落分支。

管线不可变与附件格式

项目WebGL 2WebGPU
管线状态可在绘制前修改创建时固定,改动必须新建
颜色附件格式可省略colorAttachmentFormats 必填
深度附件格式可省略depthStencilAttachmentFormat 写入深度时必填
纹理用途约束宽松创建时声明,缺一项即不可用
缓冲用途约束宽松必须写全 VERTEX | COPY_DST
间接绘制抛错drawIndirect 可用
计算着色器没有支持 compute 阶段
多画布只支持一个支持多个

这张表的每一行都会在迁移时变成 bug。WebGL 上能跑的代码, 在 WebGPU 上最常卡在附件格式与用途声明上,而不是着色器语法。

能力查询

差异不该靠猜,设备上有 features 与 limits 两组查询。 用它们决定是否启用某条路径,比按 device.type 分支更可靠。

if (device.isTextureFormatSupported('rgba32float')) {
  // 可以做浮点精度的离屏计算
}

if (device.features.has('float32-renderable-webgl')) {
  // WebGL 下浮点纹理可作为渲染目标
}

console.log(device.limits);
console.log(device.info.version, device.info.shadingLanguage);

注意事项

best-available 导致行为不一致

同一份代码在两种设备上走不同后端,画面细节、精度与性能都不同。 线上出现「这台机器上没问题」的报告时,先打印 device.type 与 device.info.renderer,不要从代码逻辑开始查。

上下文丢失

两个后端都会丢失上下文:切换显卡、系统休眠、驱动重置都会触发。 device.lost 兑现后,所有资源失效,必须重建设备并重新上传, 不能只重画一帧。

计算着色器不可移植

stage: 'compute' 只在 WebGPU 可用。需要双后端支持时, 把计算改写成片元着色器加离屏目标,用渲染管线完成同样的逐像素运算。

引用本文

经纬志(2026)。《WebGL2 与 WebGPU 的差异》。https://www.jingweizhi.com/lumagl/tutorial/webgpu-vs-webgl.html(访问日期 2026-11-03)。