three.js Backend 抽象基类详解:统一 WebGPU 与 WebGL2 双后端的渲染接口设计
Backend 是 three.js 新渲染架构(src/renderers/common/)中的抽象基类,它定义了一套封装全部后端相关逻辑的接口,使上层 Renderer 可以在 WebGPU 与 WebGL 2 两种 3D 后端之间透明切换。阅读本文后,你将完整掌握 Backend 的构造参数、属性与全部方法契约,理解 WebGPURenderer 如何通过它创建、初始化并驱动具体后端,以及纹理、缓冲、管线、时间戳查询等资源在各后端间的生命周期管理方式。
Backend 要解决的问题
在 three.js 的渲染体系中,大部分渲染逻辑(场景遍历、渲染列表组织、渲染目标切换、后处理调度等)都实现于 Renderer 模块及其配套的管理组件中,与具体 GPU API 无关。但仍有大量操作是「当前 3D 后端特有」的——创建 GPU 纹理、编译着色器管线、上传顶点缓冲、解析时间戳查询等,这些命令在 WebGPU 与 WebGL 2 中的调用方式截然不同。
Backend 抽象基类正是为此而生:它定义了一个统一接口,将上述后端相关逻辑全部收敛到这一层,每个后端的派生类必须实现该接口。这样上层代码只需面向 Backend 编程,底层即可在浏览器支持 WebGPU 时使用 WebGPUBackend,否则回退到 WebGL 2 后端的 WebGLBackend。
架构定位:谁创建并持有 Backend
从源码结构看,Backend 实例并非由用户直接创建,而是由 WebGPURenderer 在构造时根据参数选择后端类并实例化,见 WebGPURenderer.js:
constructor( parameters = {} ) {
let BackendClass;
if ( parameters.forceWebGL ) {
BackendClass = WebGLBackend;
} else {
BackendClass = WebGPUBackend;
parameters.getFallback = () => {
warn( 'WebGPURenderer: WebGPU is not available, running under WebGL2 backend.' );
return new WebGLBackend( parameters );
};
}
const backend = new BackendClass( parameters );
super( backend, parameters );
}
即:forceWebGL: true 时直接使用 WebGL 2 后端;否则默认使用 WebGPUBackend,并注册一个 getFallback 回调——当 WebGPU 初始化失败时,Renderer.js 会调用该回退逻辑重新初始化后端。Renderer 构造函数接收 backend 并存为 this.backend,后续所有后端调用均通过它转发。Renderer 内部对 backend 的调用链包括(均见 Renderer.js):
- 初始化:
backend.init( this ),失败时走 fallback 重新init(约 L798-L809); - 渲染流程:
backend.beginRender( renderContext )/backend.draw( renderObject, this.info )/backend.finishRender( renderContext )(L1890、L3902、L1908); - Compute:
backend.updateTimeStampUID( computeNodes )、inspector 的 begin/finish 使用backend.getTimestampUID()(L2913-L2982); - 资源读取与能力查询:
backend.copyTextureToBuffer、backend.getArrayBufferAsync、backend.hasFeature( name )、backend.isOccluded( ... )、backend.resolveTimestampsAsync( type )等。
这一「Renderer 管流程、Backend 管 GPU API」的分工,是理解下文的整体线索。
构造函数与核心属性
new Backend( parameters : Object )(抽象类)
构造一个新的 backend。参数为一个承载后端配置的普通对象,基类构造函数将其浅拷贝保存为 this.parameters,并初始化默认值(见 Backend.js):
| 属性 | 类型 | 说明 |
|---|---|---|
.coordinateSystem |
number(抽象、只读) |
后端使用的坐标系标识。 |
.data |
WeakMap.<Object, Object> |
弱映射容器,存放纹理、属性、渲染目标等对象的后端专属数据。 |
.domElement |
HTMLCanvasElement | OffscreenCanvas |
渲染器绘制目标画布的元素引用,默认 null。 |
.hasTimestamp |
boolean(只读) |
该后端是否支持查询时间戳。 |
.parameters |
Object |
后端的构造参数。 |
.renderer |
Renderer |
所属渲染器引用,默认 null,由 init() 注入。 |
.timestampQueryPool |
Object |
时间戳查询池引用,形如 { render: ..., compute: ... }。 |
.trackTimestamp |
boolean |
是否使用 Timestamp Query API 跟踪时间戳,默认 false。 |
其中坐标系的两个取值定义在 constants.js:WebGLCoordinateSystem = 2000 与 WebGPUCoordinateSystem = 2001,具体后端通过只读 getter 返回对应值(例如 WebGPUBackend 的 get coordinateSystem() 返回 WebGPUCoordinateSystem)。Renderer 亦通过 this.backend.coordinateSystem 对外暴露该值(Renderer.js),上层可据此判断坐标语义差异。
生命周期:init / dispose / updateSize
.init( renderer : Renderer ) : Promise(async)
初始化 backend 直至其可用。文档约定:具体后端应在该方法中实现渲染上下文创建及相关操作。基类实现(Backend.js)仅完成 this.renderer = renderer 的赋值,真正的初始化由子类扩展:
- WebGPUBackend.init() 会:若参数未提供
device,则按powerPreference、featureLevel: 'compatibility'、xrCompatible请求 adapter,遍历GPUFeatureName收集受支持特性后requestDevice;监听device.lost与device.onuncapturederror转发给renderer.onDeviceLost/renderer.onError;根据core-features-and-limits特性设置compatibilityMode(兼容模式下关闭 MSAA);最后将trackTimestamp与GPUFeatureName.TimestampQuery特性支持情况取交集,并调用this.updateSize()。 - WebGL 2 后端则在此创建
WebGL2RenderingContext及state、capabilities等工具模块(WebGLBackend.js)。
.dispose()(abstract)
释放内部资源。Renderer 在 dispose() 时先遍历 backend.timestampQueryPool 清理各查询池,再调用 backend.dispose()(Renderer.js),后端负责释放管线、缓冲、纹理等 GPU 侧资源。
.updateSize()(abstract)
后端在渲染器尺寸变化时可以覆写此方法执行自己的逻辑。Renderer.setSize 在 _initialized 为真时调用 this.backend.updateSize()(Renderer.js),例如 WebGPU 后端需要据此重建 canvas 默认 framebuffer 配置。
渲染调用流程接口
这一组方法包裹一次 render / compute 调用的首尾与执行环节:
渲染(render call)
- .beginRender( renderContext : RenderContext )(abstract):在一次渲染调用开始时执行,后端可用它准备后续 draw call 的状态。参数为本次调用的渲染上下文。
- .finishRender( renderContext : RenderContext )(abstract):渲染调用结束时执行,用于收尾工作。
- .draw( renderObject : RenderObject, info : Info )(abstract):为给定渲染对象执行一条 draw 命令。
info持有 GPU 内存与渲染过程的统计信息(Renderer.info)。 - .initRenderTarget( renderContext : RenderContext )(abstract):初始化渲染上下文中定义的渲染目标。
- .updateViewport( renderContext : RenderContext )(abstract):按渲染上下文中的值更新视口。
- .setScissorTest( boolean : boolean )(abstract):开启或关闭剪裁测试;
Renderer的setScissorTest直接转发至后端(Renderer.js)。 - .setXRTarget( xrTarget : Object ):设置 XR 渲染目标。可覆写此钩子的后端(如直接渲染进 XR framebuffer 的 WebGPU 后端)在 XR 会话切换目标时收到通知;
Renderer在退出 XR 时会调用this.backend.setXRTarget( null )(Renderer.js)。
Compute 调用(compute call)
- .beginCompute( computeGroup : Node | Array. )(abstract):compute 调用开始时执行,供后端准备后续 compute 任务的状态。
- .finishCompute( computeGroup : Node | Array. )(abstract):compute 调用结束时执行收尾。
- .compute( computeGroup, computeNode : Node, bindings : Array., computePipeline : ComputePipeline )(abstract):执行一条 compute 命令。
computeGroup是本次 compute 调用的节点组(也可以是单个 compute 节点),computeNode为具体节点,bindings为绑定数组,computePipeline为对应管线。
程序与管线接口
- .createProgram( program : ProgrammableStage )(abstract):由可编程阶段(programmable stage)创建着色器程序。
- .destroyProgram( program : ProgrammableStage )(abstract):销毁给定可编程阶段对应的着色器程序。
- .createRenderPipeline( renderObject : RenderObject, promises : Array. )(abstract):为渲染对象创建渲染管线。第二个参数是编译 Promise 数组,供
compileAsync()使用——这使管线可预先异步编译。 - .createComputePipeline( computePipeline : ComputePipeline, bindings : Array. )(abstract):为 compute 节点创建 compute 管线。
- .getRenderCacheKey( renderObject : RenderObject ) : string(abstract):返回用于识别渲染管线的缓存键。键相同意味着可复用同一管线,这是管线缓存机制的基础。
- .needsRenderUpdate( renderObject : RenderObject ) : boolean(abstract):渲染管线是否需要更新时返回
true。 - .createNodeBuilder( renderObject : RenderObject, renderer : Renderer ) : NodeBuilder(abstract):为渲染对象返回节点构建器,负责把节点材质图编译为目标后端的着色器语言(WebGPU 后端为 WGSL、WebGL 2 后端为 GLSL,见 WGSLNodeBuilder 与 GLSLNodeBuilder)。
纹理、采样器与数据传输
纹理相关的接口覆盖创建、更新、mipmap、拷贝与销毁的完整生命周期:
- .createTexture( texture : Texture, options : Object = {} )(abstract):为纹理对象在 GPU 上定义纹理。
- .updateTexture( texture : Texture, options : Object = {} )(abstract):把更新后的纹理数据上传到 GPU。
- .generateMipmaps( texture : Texture )(abstract):为纹理生成 mipmap。
- .destroyTexture( texture : Texture, isDefaultTexture : boolean = false )(abstract):销毁纹理对应的 GPU 数据;
isDefaultTexture标记该纹理是否占用默认纹理(占位纹理)。 - .createDefaultTexture( texture : Texture )(abstract):创建「默认纹理」占位符,在实际纹理就绪前供渲染引用,避免空指针。
- .updateSampler( binding : Sampler ) : string(abstract):更新纹理对应的 GPU 采样器,返回当前采样器键(用于缓存匹配)。
- .destroySampler( binding : Sampler )(abstract):释放采样器绑定的 GPU 采样器。
- .copyTextureToTexture( srcTexture, dstTexture, srcRegion : Box3 | Box2 = null, dstPosition : Vector2 | Vector3 = null, srcLevel : number = 0, dstLevel : number = 0 )(abstract):把源纹理数据拷贝到目标纹理。
srcRegion指定源端拷贝区域(缺省为整张),dstPosition为目标位置,两个 level 参数分别是源/目标 mip 层级,默认0。Renderer.copyTextureToTexture直接转发到该接口(Renderer.js)。 - .copyFramebufferToTexture( texture : Texture, renderContext : RenderContext, rectangle : Vector4 )(abstract):把当前绑定的 framebuffer 拷贝到指定纹理,
rectangle为四维向量,定义拷贝的起点与尺寸。对应Renderer层的 framebuffer 读取路径(Renderer.js)。 - .copyTextureToBuffer( texture : Texture, x, y, width, height, faceIndex : number ) : Promise.(async, abstract):把纹理数据读回为 TypedArray。参数依次为拷贝原点坐标
(x, y)、宽高width / height、以及 cube 面/深度切片/数组层索引faceIndex;返回的 Promise 在拷贝完成时以 typed array 兑现。这是Renderer读回renderTarget.textures[textureIndex]的底层实现(Renderer.js)。
顶点/存储/Uniform 缓冲接口
- .createAttribute( attribute : BufferAttribute )(abstract):为着色器属性创建 GPU 缓冲。
- .createIndexAttribute( attribute : BufferAttribute )(abstract):为索引着色器属性创建 GPU 缓冲。
- .createStorageAttribute( attribute : BufferAttribute )(abstract):为存储属性创建 GPU 缓冲(供 compute shader 读写)。
- .updateAttribute( attribute : BufferAttribute )(abstract):更新着色器属性对应的 GPU 缓冲。
- .destroyAttribute( attribute : BufferAttribute )(abstract):销毁该属性的 GPU 缓冲。
- .createUniformBuffer( uniformBuffer : Buffer )(abstract) / .destroyUniformBuffer( uniformBuffer : Buffer )(abstract):创建/销毁 uniform 缓冲。
- .updateBinding( binding : Buffer )(abstract):更新单个 buffer 绑定(例如 uniform 数值变化后重传)。
- .updateBindings( bindGroup : BindGroup, bindings : Array., cacheIndex : number, version : number )(abstract):按版本更新整个 bind group 定义,
cacheIndex与version供后端做缓存失效判断。 - .createBindings( bindGroup : BindGroup, bindings : Array., cacheIndex : number, version : number )(abstract):由 bind group 定义创建绑定,参数语义同上。
- .deleteBindGroupData( bindGroup : BindGroup )(abstract):删除与 bind group 关联的 GPU 数据。
- .getArrayBufferAsync( attribute : StorageBufferAttribute ) : Promise.(async):执行 readback 操作,把存储缓冲属性的数据从 GPU 移回 CPU;Promise 在数据就绪时以 buffer 兑现。WebGPU 后端的实现(WebGPUBackend.getArrayBufferAsync)支持
target / offset / count扩展参数并借助ReadbackBuffer复用中间缓冲。
时间戳、遮挡与能力查询
时间戳体系
- .trackTimestamp:开关属性,默认
false;WebGPU 后端在init时还会与TimestampQuery特性支持取交集(WebGPUBackend.js)。 - .timestampQueryPool:
{ render: ?, compute: ? }两个查询池,分别对应TimestampQuery.RENDER/TimestampQuery.COMPUTE(见 Backend.js)。 - .hasTimestamp(只读):后端是否支持时间戳查询。基类默认返回
false,WebGPUBackend 返回true。 - .updateTimeStampUID( abstractRenderContext : RenderContext | ComputeNode ):为渲染上下文分配唯一标识(timestampUID),供分配遮挡查询、时间戳查询等资源。基类实现的 ID 格式可从源码看到(Backend.js):
'r:'/'c:'前缀区分渲染与 compute,再拼上各自的帧内调用序号、上下文id与全局帧号,例如r:3:42:f512。 - .getTimestampUID( abstractRenderContext ) : string:读取上述唯一标识。
Renderer在 begin/finish render 与 compute 时用它驱动 inspector(Renderer.js)。 - ._getQueryPool( uid : string ):按 uid 前缀(
c:→ compute,否则 render)取出对应查询池。 - .getTimestamp( uid : string ) : number / .hasTimestampQuery( uid : string ) : boolean:查询某个 uid 的时间戳值/是否已有时间戳查询。
- .getTimestampFrames( type : string ) : Array.:返回指定类型全部时间戳帧;池不存在时返回空数组。
- .resolveTimestampsAsync( type : string = 'render' ) : Promise.(async, abstract):解析指定渲染上下文类型的时间戳。注意基类中的实际保护逻辑(Backend.js):若
trackTimestamp为false则打印一次性警告并直接返回;否则调用查询池的resolveQueriesAsync(),把总时长写回this.renderer.info[ type ].timestamp并返回。
遮挡查询
- .isOccluded( renderContext : RenderContext, object : Object3D ) : boolean(abstract):判断某 3D 对象是否被场景中的其他对象完全遮挡,文档要求后端必须借助 Occlusion Query API 实现。
Renderer在 L2452 将其转发给上层。
能力与兼容性
- .hasFeature( name : string ) : boolean(abstract):同步判断某特性(如
'shader-f16'、'bgra8-storage'等)是否受支持。 - .hasFeatureAsync( name : string ) : Promise.(async, abstract):异步版本,返回布尔 Promise。
- .hasCompatibility( name : string ) : boolean(abstract):判断后端是否具备某种「兼容性」(与特性不同,兼容性指行为差异的补偿)。WebGPU 后端内置了一张
_compatibility表,例如Compatibility.TEXTURE_COMPARE在 Android 上判定为不可用(WebGPUBackend.js)。
画布、上下文与数据容器
- .getContext() : Object(abstract):返回后端的渲染上下文对象(WebGPU 为
GPUCanvasContext,WebGL 2 为WebGL2RenderingContext)。 - .getDomElement() : HTMLCanvasElement:返回 DOM 画布元素;若
domElement尚不存在,则优先取parameters.canvas,否则调用createCanvasElement()新建,并设置data-engine="three.js r{REVISION} webgpu"属性(OffscreenCanvas 无setAttribute时跳过,见 Backend.js)。 - .getDrawingBufferSize() : Vector2:返回绘制缓冲尺寸;基类实现是转发
this.renderer.getDrawingBufferSize()并复用模块级临时Vector2。 - .getClearColor() : Color4:把渲染器清屏色与 alpha 合并为单个
Color4对象返回,同样复用模块级临时对象以避免每帧分配。
data WeakMap 容器
Backend.data 是「逻辑对象 → GPU 侧数据字典」的核心容器,四个配套方法均已由基类实现(Backend.js):
- .set( object, value ):为对象存入数据字典;
- .get( object ) : Object:取字典;若不存在则自动创建空字典并写入(懒初始化语义);
- .has( object ) : boolean:是否已有字典;
- .delete( object ):从内部结构中删除该对象。
具体后端普遍用这套接口缓存 GPU 资源,例如 WebGPU 后端用 this.get( renderTarget.texture ) 存放 GPUTexture、format 等(WebGPUBackend.js)。选用 WeakMap 意味着当 three.js 侧对象被 GC 时,后端数据引用也自动可回收,避免显式反注册。
两个具体后端实现对照
| 维度 | WebGPUBackend | WebGLBackend |
|---|---|---|
| 类型标记 | isWebGPUBackend = true |
isWebGLBackend = true |
coordinateSystem |
WebGPUCoordinateSystem(2001) |
WebGLCoordinateSystem(2000) |
hasTimestamp |
true |
基类默认 false(除非扩展) |
init() |
请求 adapter/device、监听设备丢失与未捕获错误、计算 compatibilityMode、调用 updateSize() |
创建 WebGL2RenderingContext 及 state / capabilities / extensions 等模块 |
| 着色器语言 | WGSL(WGSLNodeBuilder) |
GLSL(GLSLNodeBuilder) |
| 特色能力 | setXRRenderTargetTextures() 注册 XR 外部纹理;getArrayBufferAsync 支持 ReadbackBuffer 复用 |
VAO 缓存(vaoCache)、transform feedback 对象缓存等 WebGL 状态管理 |
两者共同验证了文档的「派生类必须实现接口」这一契约:所有标注 abstract 的方法在基类中多为空实现或返回保守默认值(hasTimestamp 返回 false、hasCompatibility 返回 false),真正语义由各后端补齐。
小结与延伸阅读
Backend 的价值在于把「面向 GPU API 的命令」与「面向场景/渲染流程的逻辑」彻底分层:Renderer 持有流程、调度与 fallback 策略,Backend 及其派生类持有资源生命周期与 API 细节。阅读路径建议:先读 Backend.js 掌握接口全集,再看 Renderer.js 中的调用点理解触发时机,最后对照 WebGPUBackend.js 与 WebGLBackend.js 观察同一接口的两种实现差异。相关文档可参考 Renderer。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00