首页
/ three.js Backend 抽象基类详解:统一 WebGPU 与 WebGL2 双后端的渲染接口设计

three.js Backend 抽象基类详解:统一 WebGPU 与 WebGL2 双后端的渲染接口设计

2026-09-04 15:46:35作者:魏献源Searcher

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.copyTextureToBufferbackend.getArrayBufferAsyncbackend.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.jsWebGLCoordinateSystem = 2000WebGPUCoordinateSystem = 2001,具体后端通过只读 getter 返回对应值(例如 WebGPUBackendget 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,则按 powerPreferencefeatureLevel: 'compatibility'xrCompatible 请求 adapter,遍历 GPUFeatureName 收集受支持特性后 requestDevice;监听 device.lostdevice.onuncapturederror 转发给 renderer.onDeviceLost / renderer.onError;根据 core-features-and-limits 特性设置 compatibilityMode(兼容模式下关闭 MSAA);最后将 trackTimestampGPUFeatureName.TimestampQuery 特性支持情况取交集,并调用 this.updateSize()
  • WebGL 2 后端则在此创建 WebGL2RenderingContextstatecapabilities 等工具模块(WebGLBackend.js)。

.dispose()(abstract)

释放内部资源。Rendererdispose() 时先遍历 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):开启或关闭剪裁测试;RenderersetScissorTest 直接转发至后端(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,见 WGSLNodeBuilderGLSLNodeBuilder)。

纹理、采样器与数据传输

纹理相关的接口覆盖创建、更新、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 层级,默认 0Renderer.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 定义,cacheIndexversion 供后端做缓存失效判断。
  • .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(只读):后端是否支持时间戳查询。基类默认返回 falseWebGPUBackend 返回 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):若 trackTimestampfalse 则打印一次性警告并直接返回;否则调用查询池的 resolveQueriesAsync(),把总时长写回 this.renderer.info[ type ].timestamp 并返回。

遮挡查询

  • .isOccluded( renderContext : RenderContext, object : Object3D ) : boolean(abstract):判断某 3D 对象是否被场景中的其他对象完全遮挡,文档要求后端必须借助 Occlusion Query API 实现。RendererL2452 将其转发给上层。

能力与兼容性

  • .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() 创建 WebGL2RenderingContextstate / capabilities / extensions 等模块
着色器语言 WGSL(WGSLNodeBuilder GLSL(GLSLNodeBuilder
特色能力 setXRRenderTargetTextures() 注册 XR 外部纹理;getArrayBufferAsync 支持 ReadbackBuffer 复用 VAO 缓存(vaoCache)、transform feedback 对象缓存等 WebGL 状态管理

两者共同验证了文档的「派生类必须实现接口」这一契约:所有标注 abstract 的方法在基类中多为空实现或返回保守默认值(hasTimestamp 返回 falsehasCompatibility 返回 false),真正语义由各后端补齐。

小结与延伸阅读

Backend 的价值在于把「面向 GPU API 的命令」与「面向场景/渲染流程的逻辑」彻底分层:Renderer 持有流程、调度与 fallback 策略,Backend 及其派生类持有资源生命周期与 API 细节。阅读路径建议:先读 Backend.js 掌握接口全集,再看 Renderer.js 中的调用点理解触发时机,最后对照 WebGPUBackend.jsWebGLBackend.js 观察同一接口的两种实现差异。相关文档可参考 Renderer

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388