Device and render pipelines
Quick answer
This tutorial restructures rendering around the v9 Device abstraction, where every GPU resource is created from the device and destroyed in child-before-parent order. It compiles separate vertex and fragment Shader objects, assembles a RenderPipeline with bufferLayout and parameters, attaches a Buffer through a VertexArray, and draws inside a beginRenderPass. It also queries limits, renderer info and texture format support. Readers can replace v8 Program code with the shader and pipeline flow.
Key points
- Compile separate vertex and fragment Shader objects before building the pipeline.
- Pass Shader objects to createRenderPipeline rather than raw source strings.
- Destroy resources child first, then call device.destroy() last.
Example
<!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
}
});
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')
});
const pipeline = device.createRenderPipeline({
vs: vs,
fs: fs,
topology: 'triangle-list',
bufferLayout: [
{ name: 'positions', stepMode: 'vertex', format: 'float32x3' }
],
parameters: {
depthCompare: 'less-equal',
depthWriteEnabled: true
}
});
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);
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>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();
Notes
- A pipeline may still be pending, so check isPending and linkStatus before drawing.
- The WebGL backend binds one canvas per device, and a second createCanvasContext throws.
English edition written for Atlaslog. Code is taken from the original Chinese page and runs unchanged.
Atlaslog (2026). Device and render pipelines. https://www.jingweizhi.com/en/lumagl/tutorials/device-pipeline.html (accessed 2026-11-03).