Device 与渲染管线
luma.gl 9 把所有 GPU 资源收到一个设备对象下,绘制不再是「用 program 画」, 而是「编译着色器、组装管线、在渲染通道里绘制」。三者的生命周期互相独立, 释放顺序也要跟着这个结构走。
v9 的资源结构
| 对象 | 由谁创建 | 负责什么 | 对应原生 WebGL |
|---|---|---|---|
Device | luma.createDevice() | 上下文、画布、资源工厂 | WebGL2RenderingContext |
Shader | device.createShader() | 单个阶段的编译(顶点或片元) | createShader / compileShader |
RenderPipeline | device.createRenderPipeline() | 着色器 + 布局 + 固定状态 | linkProgram 加状态设置 |
VertexArray | device.createVertexArray() | 缓冲区与属性的绑定 | vertexAttribPointer |
RenderPass | device.beginRenderPass() | 渲染目标、清除、绘制命令 | bindFramebuffer / clear / drawArrays |
v8 的 Program 在 v9 中被移除。它原本同时承担「着色器链接」与
「uniform 与 attribute 管理」,在 WebGPU 上无法对应:
WebGPU 的管线是不可变对象,着色器源码、顶点布局、深度与混合状态必须一次性确定。
v9 因此把它拆成 Shader 与 RenderPipeline,
日常绘制再由 @luma.gl/engine 的 Model 封装。
完整代码:一个三角形
UMD 版本没有内置适配器,必须按顺序加载四个包:core 提供设备与资源、
webgl 提供 WebGL2 适配器、shadertools 提供着色器组装、
engine 提供 Model 与 AnimationLoop。
全局对象是 luma,创建设备的入口写成 luma.luma.createDevice,
适配器写 luma.webgl2Adapter。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<style>
html, body { height: 100%; margin: 0; background: #0d1117; }
#cv { display: block; width: 100%; height: 100%; }
</style>
</head>
<body>
<canvas id="cv"></canvas>
<script src="https://cdn.jsdelivr.net/npm/@luma.gl/core@9.4.2/dist/dist.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@luma.gl/webgl@9.4.2/dist/dist.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@luma.gl/shadertools@9.4.2/dist/dist.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@luma.gl/engine@9.4.2/dist/dist.min.js"></script>
<script>
(async function () {
const device = await luma.luma.createDevice({
type: 'webgl',
adapters: [luma.webgl2Adapter],
createCanvasContext: {
canvas: document.getElementById('cv'),
useDevicePixels: Math.min(window.devicePixelRatio, 2),
autoResize: true
}
});
// 1. 两个阶段分别编译
const vs = device.createShader({
stage: 'vertex',
source: [
'#version 300 es',
'in vec3 positions;',
'void main() { gl_Position = vec4(positions, 1.0); }'
].join('\n')
});
const fs = device.createShader({
stage: 'fragment',
source: [
'#version 300 es',
'precision highp float;',
'out vec4 fragColor;',
'void main() { fragColor = vec4(0.35, 0.80, 0.62, 1.0); }'
].join('\n')
});
// 2. 管线固定拓扑、布局与状态
const pipeline = device.createRenderPipeline({
vs: vs,
fs: fs,
topology: 'triangle-list',
bufferLayout: [
{ name: 'positions', stepMode: 'vertex', format: 'float32x3' }
],
parameters: {
depthCompare: 'less-equal',
depthWriteEnabled: true
}
});
// 3. 顶点数组把缓冲区挂到属性上
const positions = device.createBuffer(new Float32Array([
-0.6, -0.45, 0,
0.6, -0.45, 0,
0.0, 0.55, 0
]));
const vertexArray = device.createVertexArray({
shaderLayout: pipeline.shaderLayout,
bufferLayout: pipeline.bufferLayout
});
const slot = vertexArray.getBufferSlot('positions');
if (slot !== null) vertexArray.setBuffer(slot, positions);
// 4. 通道里绘制
const pass = device.beginRenderPass({
clearColor: [0.05, 0.07, 0.10, 1]
});
pass.setPipeline(pipeline);
pass.setVertexArray(vertexArray);
pass.draw({ vertexCount: 3 });
pass.end();
})();
</script>
</body>
</html>
createShader 的 source 必须是完整的单阶段源码,
包含 #version 300 es 与 precision 声明。
createRenderPipeline 接收的是 Shader 对象,
传源码字符串不会自动编译,管线拿到的是无效引用。
运行期查询与释放
设备上挂着能力查询与上限,用来在运行期决定分支。格式支持与否必须在创建资源前问清楚, 写死的格式在部分显卡上会直接失败。
console.log(device.type); // 'webgl'
console.log(device.info.renderer); // 显卡型号
console.log(device.info.shadingLanguage); // 'glsl'
console.log(JSON.stringify(device.limits)); // 各类上限
if (device.isTextureFormatSupported('rgba16float')) {
// 可以创建半浮点离屏目标
}
// 释放顺序:先子后父
pipeline.destroy();
vertexArray.destroy();
positions.destroy();
device.destroy();
device.destroy() 会让它创建的所有资源失效,之后继续绘制拿到的是空句柄。
上下文丢失时 device.lost 兑现,需要重建设备并重新上传资源。
注意事项
拿 v8 的 Program 写法硬套
new Program(gl, {vs, fs}) 在 v9 里没有对应类。
迁移时把每个 program 拆成两个 Shader 加一条
RenderPipeline,把原来在绘制前设置的深度、混合状态搬进
parameters。
管线创建后立刻绘制
链接状态可能是 pending。首帧出现空洞时先查
pipeline.isPending 与 linkStatus,
不要急着怀疑顶点数据或布局。
设备只建一次
WebGL 后端一个设备只挂一个画布,重复调用 createCanvasContext 会抛错。
多个视图需要共享上下文时,用同一台设备加不同的帧缓冲,
而不是建多台设备。
经纬志(2026)。《Device 与渲染管线》。https://www.jingweizhi.com/lumagl/tutorial/device-pipeline.html(访问日期 2026-11-03)。