首页
/ Electron sharedTexture.subtle 实战指南:SharedTextureSubtle 对象的导入、跨进程传输与 GPU 资源生命周期管理

Electron sharedTexture.subtle 实战指南:SharedTextureSubtle 对象的导入、跨进程传输与 GPU 资源生命周期管理

2026-09-06 16:08:02作者:柯茵沙

SharedTextureSubtle 是 Electron sharedTexture 模块面向高级用户提供的「subtle(底层)」API 集合,用于将 Windows NT HANDLE、macOS IOSurface、Linux NativePixmap 等平台原生共享纹理句柄导入 Electron 并转换为 VideoFrame,且支持跨进程传输。读完本文,你能掌握 importSharedTexture / finishTransferSharedTexture 的完整参数结构、SharedTextureImportedSubtle 上五个核心方法的用法,以及基于同步令牌(Sync Token)与释放回调的 GPU 资源生命周期管理方案,并能在离屏渲染(OSR)场景中把纹理送入 WebGPU 渲染管线。

一、背景:subtle API 在整个 sharedTexture 体系中的位置

Electron 的 sharedTexture 模块(见 sharedTexture 模块文档)负责把外部共享纹理导入 Electron,并转换为 Web 标准的 VideoFrame,支持所有 Web 渲染体系(WebGPU、WebGL 等),且纹理引用可以跨 Electron 进程传递。该模块的常规 API 有三个:

  • sharedTexture.importSharedTexture(options):仅主进程可用,返回 SharedTextureImported,通过 allReferencesReleased 回调在所有进程引用全部释放时被通知;
  • sharedTexture.sendSharedTexture(options, ...args):仅主进程可用,把已导入的纹理发送给指定 WebFrameMain 对应的渲染进程(有 1000ms 超时,要求接收端已注册接收回调);
  • sharedTexture.setSharedTextureReceiver(callback):仅渲染进程可用,注册接收纹理的回调。

sharedTexture.subtle 是一个 Experimental(实验性) 属性,返回一个 SharedTextureSubtle 对象,为高级用户提供更细粒度的控制:它不依赖 importSharedTexture 的托管式生命周期(allReferencesReleased),而是把「导入、转移、同步、释放」每一步都暴露为显式方法,让用户自己决定何时、如何释放 GPU 进程中的底层资源。正如模块文档所述,实验性 API 标记为 Experimental,未来可能变化或移除,使用时应以当前仓库版本为准。

模块文档同时指向了一份设计说明 shell/common/api/shared_texture/README.md,其中解释了实现基于 Chromium 的 SharedImage 基础设施,以及为何要引入同步令牌来管理帧的跨进程生命周期——这正是下文第四节展开的原理依据。

二、SharedTextureSubtle 对象:两个核心方法

SharedTextureSubtle 对象定义在 docs/api/structures/shared-texture-subtle.md,包含两个方法:

2.1 sharedTexture.subtle.importSharedTexture(textureInfo)

它的作用是从给定选项导入共享纹理。注意与托管版 sharedTexture.importSharedTexture(options) 的差异:subtle 版本只接收 textureInfo,不提供 allReferencesReleased 回调;所有进程间引用的释放都要由你显式管理。典型输入来源是 OSR paint 事件中的 event.texture.textureInfo(见 OffscreenSharedTexture 结构,其中 handle 字段即 SharedTextureHandle)。

2.2 sharedTexture.subtle.finishTransferSharedTexture(transfer)

它与 startTransferSharedTexture 配对使用:源进程用 startTransferSharedTexture 生成可序列化的 SharedTextureTransfer,把 transfer 字符串(不透明数据)经 IPC 等任意通道传送到目标进程,目标进程调用 finishTransferSharedTexture 即可在本进程重新获得对同一 GPU 纹理的引用。从 SharedTextureTransfer 文档看,其字段(transfersyncTokenpixelFormatcodedSizevisibleRecttimestamp)均为 Readonly,transfer 字符串「可以在 Electron 进程之间传递」。

从源码设计文档 shell/common/api/shared_texture/README.md 可以确认其底层机制:实现使用 SharedImageInterface->ImportSharedImageClientSharedImage->Export 序列化足够信息,使另一进程能重新获取指向同一 SharedImageBacking(GPU 进程中的 Mailbox 引用)的 SharedImage,并复用 mojo 序列化器把结果序列化为字符串——这就是 transfer 字段可以裸串跨进程传输的原因。

三、导入输入结构:SharedTextureImportTextureInfo

importSharedTexture 接收的 SharedTextureImportTextureInfo 完整字段如下,是编写纹理导入代码时的关键参数清单:

字段 类型 必填 说明
pixelFormat string 纹素的像素格式,取值见下表
colorSpace ColorSpace 纹理的色域
codedSize Size 共享纹理的完整尺寸
visibleRect Rectangle [0, 0, codedSize.width, codedSize.height] 的子区域;常见情况下就是全区域
timestamp number 以微秒为单位的时间戳,会反映到 VideoFrame
handle SharedTextureHandle 共享纹理句柄

pixelFormat 支持六种格式:

  • bgra:32bpp BGRA(字节序),1 平面;
  • rgba:32bpp RGBA(字节序),1 平面;
  • rgbaf16:半浮点(half float)RGBA,1 平面;
  • nv12:12bpp,Y 平面后跟 2x2 交错 UV 平面;
  • nv16:16bpp,Y 平面后跟 2x1 交错 UV 平面;
  • p010le:4:2:0 10-bit YUV(小端),Y 平面后跟 2x2 交错 UV 平面。

其中 YUV 系格式(nv12/nv16/p010le)正是视频帧的典型编码,而 OSR 导出的纹理则为 rgba/bgra/rgbaf16(见 OffscreenSharedTexture 文档中 textureInfo.pixelFormat 的枚举)。

平台句柄:SharedTextureHandle

handle 字段是平台相关的 SharedTextureHandle

  • WindowsntHandle(Buffer)持有共享纹理。注意该 NT HANDLE 仅在当前进程内有效;rgbabgrargbaf16 格式的输出纹理句柄不带 keyed mutex,而 nv12 格式的句柄带 keyed mutex。
  • macOSioSurface(Buffer)持有 IOSurfaceRef;该 IOSurface 是当前进程本地的(非全局)。
  • LinuxnativePixmap 对象包含各平面信息——planes 数组中每个平面含 strideoffsetsize(字节)与 fd(底层内存对象(通常是 dmabuf)的文件描述符);另有 modifier(来自 GBM 库,传给 EGL 驱动)与 supportsZeroCopyWebGpuImport(是否支持零拷贝导入 WebGPU)。

这里有一个非常重要的前提约束(由设计文档 shell/common/api/shared_texture/README.md 明确说明):调用 importSharedTexture 时,句柄必须已经对当前进程可见。Windows 的 NT HANDLE 是进程本地的,跨进程需要 DuplicateHandle;macOS 的全局 IOSurface(kIOSurfaceIsGlobal)是已废弃选项,更常规的方式是把 mach_port 经 IPC 传递——而 Chromium 的 IPC 内部已透明处理这些问题(这也是 OSR paint 事件能直接使用句柄的原因,因为句柄已经过 IPC 传输)。另外,Windows 上不能使用非 NT HANDLE(对其调用 CloseHandle 是非法操作会导致 Chromium 崩溃),Electron 在导入时会对 NT HANDLE 做 duplicate。

四、输出对象:SharedTextureImportedSubtle 的五个方法

importSharedTexturefinishTransferSharedTexture 都返回 SharedTextureImportedSubtle。它提供五个方法,覆盖了「取帧、释放、再转移、同步」的完整生命周期:

4.1 getVideoFrame()VideoFrame

创建使用当前进程中已导入共享纹理的 VideoFrame。用完后可调用 VideoFrame.close();文档特别指出「底层资源会在内部等待 GPU 完成」,即不需要手动等待 GPU 命令缓冲执行完毕即可 close 帧。

4.2 release(callback?) —— 释放与 GPU 完成通知

释放该对象的资源。如果一次传输后获得了多个 SharedTextureImported 对象(subtle 场景下即多个 SharedTextureImportedSubtle),必须逐个 release,最后一个释放时 GPU 进程中的资源才会真正销毁。

可选的 callback 参数在 GPU 命令缓冲完成使用该共享纹理时触发,提供了一个精确的时机来安全释放依赖资源。文档给出的两个典型场景:

  • 若本对象由 finishTransferSharedTexture 创建,可以用该回调通知源进程安全释放当初调用 startTransferSharedTexture 的原始对象;
  • 也可以安全释放当初用于 importSharedTexture 的源共享纹理。

4.3 startTransferSharedTexture()SharedTextureTransfer

创建一个可序列化、可转移到其他进程的 SharedTextureTransfer。把其中的 transfer 字符串通过 IPC 发给目标进程后,对方用 finishTransferSharedTexture 取回 SharedTextureImportedSubtle

4.4 getFrameCreationSyncToken()SharedTextureSyncToken

面向高级用户。通常应在 finishTransferSharedTexture 之后调用,得到的 SharedTextureSyncToken(内含一个不透明的 syncToken 字符串)应回传给调用过 startTransferSharedTexture 的源对象,目的是防止源对象在目标对象尚未于 GPU 进程中异步拿到引用之前就释放底层资源。

4.5 setReleaseSyncToken(syncToken)

面向高级用户。设置后,本对象的底层资源不会释放,直到所设置的 SharedTextureSyncToken 在 GPU 进程中被满足(signaled)。文档说明其价值:使用同步令牌后,用户不再需要使用 release 回调来做生命周期管理——两种机制(回调与令牌)可以二选一,或组合使用。

底层原理:为什么需要 Sync Token

设计文档 shell/common/api/shared_texture/README.md 对这一机制有详细解释:GPU 调用大多是异步的,通过各客户端进程的 GpuChannel 提交到 GPU 进程的命令缓冲。当两个进程持有引用同一 Mailbox 的两个 SharedImage 实例时,无法直接知道 GPU 何时用完资源,这通常由 SyncToken 保证——例如可以把主进程中 SharedImage 的销毁调度到一个空 SyncToken 上;而在渲染进程中用 WebGPU(或 WebGL)管线使用同一纹理时,WebGPU 会生成销毁令牌,确保渲染完成前不会销毁。实现上,Electron 使用 gpu::ContextSupport 注册「某个 SyncToken 被释放(signaled)」的回调:当你对已导入的共享纹理对象调用 release() 且它已通过 VideoFrame 进入 WebGPU 管线时,Electron 会等待 WebGPU 渲染完成,然后运行你的回调,通知你去释放依赖资源(如主进程中的原始导入对象、源纹理等)。

五、端到端实战:OSR 纹理经 subtle API 跨进程送入 WebGPU 渲染

仓库测试用例 spec/api-shared-texture-spec.ts 中的 "successfully imported and rendered with subtle api" 用例(约第 34 行起)完整演示了 subtle API 的工作流,配套的 preload/renderer 代码在 spec/fixtures/api/shared-texture/subtle/ 目录下。主进程侧的关键步骤(摘自 spec/api-shared-texture-spec.ts):

// 1. 用 OSR 开启共享纹理导出,获得纹理句柄来源
const osr = new BrowserWindow({
  width: 128,
  height: 128,
  webPreferences: { offscreen: { useSharedTexture: true } }
});
osr.webContents.setFrameRate(1);

osr.webContents.on('paint', (event) => {
  // Step 1: 纹理句柄输入源(来自 OSR paint 事件)
  const texture = event.texture;

  // Step 2: subtle 导入 —— 只传 textureInfo,返回 SharedTextureImportedSubtle
  const importedSubtle = sharedTexture.subtle.importSharedTexture(texture.textureInfo);

  // Step 3: 生成可序列化传输对象
  const transfer = importedSubtle.startTransferSharedTexture();

  // Step 4: 把 transfer 字符串经 IPC 发给渲染进程
  win.webContents.send('shared-texture', id, transfer);
});

ipcMain.on('shared-texture-done', (event, id) => {
  const { importedSubtle, texture } = capturedTextures.get(id);

  // Step 13: 释放主进程侧的导入对象
  // Step 14: 在 GPU 完成使用后(release 回调内)释放源纹理
  importedSubtle.release(() => {
    texture.release();
  });
});

渲染进程侧(spec/fixtures/api/shared-texture/subtle/preload.jsspec/fixtures/api/shared-texture/subtle/renderer.js):

// preload.js —— 接收传输对象并恢复纹理
ipcRenderer.on('shared-texture', async (e, id, transfer) => {
  // Step 5: 从 transfer 恢复出 SharedTextureImportedSubtle
  const importedSubtle = sharedTexture.subtle.finishTransferSharedTexture(transfer);
  await cb(id, importedSubtle);

  // Step 10: 带回调释放;GPU 命令缓冲完成后通知主进程
  importedSubtle.release(() => {
    ipcRenderer.send('shared-texture-done', id);
  });
});

// renderer.js —— 取帧并用 WebGPU 渲染
const frame = importedSubtle.getVideoFrame();   // Step 7: 获取 VideoFrame
await window.renderFrame(frame);                 // Step 8: WebGPU 渲染
frame.close();                                   // Step 9: 用完关闭帧

这个链路验证了 subtle API 的完整闭环:OSR paint 事件产出平台句柄 → 主进程 importSharedTexturestartTransferSharedTexture 生成可跨进程传输的 SharedTextureTransfer → 渲染进程 finishTransferSharedTexture 恢复引用 → getVideoFrame 送入 WebGPU 管线 → 两级 release 回调按「GPU 完成」事件逐级向上游释放资源,确保主进程侧的源纹理(texture.release())一定在所有 GPU 使用完成之后才被释放。测试最终通过截屏像素比对(误差 < 1%)验证渲染结果正确。

六、适用前提、限制与选型建议

  1. 实验性 APIsharedTexture.subtle 及其相关结构均标记为 Experimental,未来可能调整或移除;且当前测试用例 spec/api-shared-texture-spec.ts 中注明「目前仅在 macOS arm64 上能正确运行」(process.platform !== 'darwin' || process.arch !== 'arm64' 时整体跳过),其他平台的可用性请以当前仓库实测为准。
  2. 句柄可见性前提:调用 importSharedTexture 时平台句柄必须已对当前进程可用(Windows 的 NT HANDLE 为进程本地、macOS 的 IOSurface 为进程本地引用);若要在渲染进程直接导入,需自行保证句柄在该进程内可见(设计文档建议优先利用 Electron/Chromium IPC 的透明句柄传递能力)。
  3. 生命周期责任划分
    • 追求简单、由 Electron 托管生命周期(所有引用释放后自动回调)→ 用托管版 sharedTexture.importSharedTexture + sendSharedTexture / setSharedTextureReceiver
    • 需要精确控制跨进程转移与 GPU 释放时机(例如自己维护 IPC 通道、自定义释放顺序)→ 用 sharedTexture.subtle,配合 startTransferSharedTexture / finishTransferSharedTexture 手动传输,并用 release(callback)getFrameCreationSyncToken + setReleaseSyncToken 二选一做生命周期管理。
  4. 多引用释放规则:一次传输后若得到多个导入对象,必须每个都调用 release,最后一个释放时 GPU 进程中的资源才销毁。
  5. 配套文档sharedTexture 模块总览见 docs/api/shared-texture.md;设计原理(SharedImage、SyncToken、跨进程句柄处理)见 shell/common/api/shared_texture/README.md;相关结构文档 SharedTextureImportedSubtleSharedTextureTransferSharedTextureSyncTokenSharedTextureHandleSharedTextureImportTextureInfo 均位于 docs/api/structures/ 目录下,可作为参数参考的权威来源。
登录后查看全文
热门项目推荐
相关项目推荐