首页
/ Electron SharedTextureImported 对象详解:跨进程共享纹理导入、VideoFrame 渲染与 GPU 资源生命周期管理

Electron SharedTextureImported 对象详解:跨进程共享纹理导入、VideoFrame 渲染与 GPU 资源生命周期管理

2026-09-06 16:01:50作者:郦嵘贵Just

SharedTextureImported 是 Electron sharedTexture 模块(实验性 API)中“受管(managed)”导入接口的核心返回对象:它代表一个已被导入到当前进程、可供渲染使用的跨进程共享纹理。读完本文,你将理解该对象四个成员(textureIdgetVideoFramereleasesubtle)的确切语义、它与 SharedTextureImportedSubtle 低级 API 的关系、它在主进程与渲染进程之间如何序列化传输,以及背后的 Chromium SharedImage/SyncToken 资源生命周期设计。

一、SharedTextureImported 在 sharedTexture 模块中的位置

sharedTexture 模块文档sharedTexture 描述为“把共享纹理导入 Electron 并把平台特定句柄转换为 VideoFrame 的模块,支持所有 Web 渲染系统,且可跨 Electron 进程传输”。模块提供两组 API:

组别 主要入口 适用进程 返回对象
受管(managed)API sharedTexture.importSharedTexture(options) 主进程 SharedTextureImported
受管(managed)API sharedTexture.sendSharedTexture(options, ...args) 主进程 Promise<void>
受管(managed)API sharedTexture.setSharedTextureReceiver(callback) 渲染进程
精细(subtle)API sharedTexture.subtle.* 主进程与渲染进程 SharedTextureImportedSubtle

SharedTextureImportedimportSharedTexture 在主进程中创建,其成员定义完整收录于 结构文档。它同时承担两个职责:一是把纹理以标准 VideoFrame 形式暴露给 Web 渲染栈;二是作为跨进程传输的载体(通过 sendSharedTexture 序列化到渲染进程,对端仍会得到一个 SharedTextureImported 实例)。

所有相关 API 均标注为 Experimental,官方明确提示“可能在未来被移除”,生产环境使用时需锁定 Electron 版本并保留回退路径。

二、对象成员逐一解析

2.1 textureId:导入纹理的全局标识

  • textureId string — 导入的共享纹理的唯一标识符。

该 ID 在 JS 层有明确的实现痕迹。渲染进程端的受管封装 lib/renderer/api/shared-texture.ts 构造出暴露给应用代码的 SharedTextureImported 包装对象,textureId 直接取自传输通道传来的 id 参数,并在 release 回调中作为凭证回传主进程(IPC_MESSAGES.IMPORT_SHARED_TEXTURE_RELEASE_RENDERER_TO_MAIN)。也就是说,textureId 是主进程侧引用计数表的主键:渲染进程释放引用时,主进程据此记账,只有当所有进程的引用都释放后,底层 GPU 资源才会真正销毁。

2.2 getVideoFrame():以标准 VideoFrame 消费纹理

  • getVideoFrame Function<VideoFrame> — 在当前进程中创建一个使用已导入共享纹理的 VideoFrame。使用完毕后可调用 VideoFrame.close(),底层资源会在内部等待 GPU 完成(wait for GPU finish)。

这是把“裸句柄”变成 Web 标准对象的关键一步。VideoFrame 可以直接喂给 WebGPU 的 importExternalTexture/VideoFrame 导入路径,也可以用于 OffscreenCanvas 等场景。文档强调两点使用纪律:

  1. 必须调用 VideoFrame.close()VideoFrame 持有对纹理的引用,不关闭会造成纹理引用无法归零,最终使 release 永远无法完成;
  2. GPU 完成等待是内建的getVideoFrame 之后立即 release 是安全的——释放路径会经过 SyncToken 机制等待 GPU 命令缓冲执行完毕(见第五节设计原理)。

2.3 release():引用计数式释放

  • release Function — 释放本对象对该共享纹理的引用。底层资源在所有引用释放前会一直存活。

“所有引用”是跨进程维度的。受管 API 下,主进程 importSharedTextureoptions.allReferencesReleased 回调正是在所有进程的所有引用都释放后触发,官方建议“在此回调被调用之前,应保持源纹理有效”。典型时序(与 spec/api-shared-texture-spec.ts 的受管测试步骤一致):

// 主进程:OSR paint 事件提供外部纹理
osr.webContents.on('paint', async (event) => {
  const texture = event.texture; // OffscreenSharedTexture

  // 1. 导入(受管 API)
  const imported = sharedTexture.importSharedTexture({
    textureInfo: texture.textureInfo,
    allReferencesReleased: () => {
      // 4. 所有进程引用归零、GPU 完成后才安全释放源纹理
      texture.release();
    }
  });

  // 2. 传输到渲染进程(渲染进程需先注册 receiver)
  await sharedTexture.sendSharedTexture({
    frame: win.webContents.mainFrame,
    importedSharedTexture: imported
  });

  // 3. 立即释放主进程侧引用是安全的
  imported.release();
});
// 渲染进程:接收并渲染
const { sharedTexture } = require('electron');

sharedTexture.setSharedTextureReceiver(async (data, ...args) => {
  const imported = data.importedSharedTexture; // SharedTextureImported

  const frame = imported.getVideoFrame(); // VideoFrame
  // ... 送入 WebGPU / OffscreenCanvas 渲染 ...
  frame.close();
  imported.release(); // 释放渲染进程侧引用
});

两个容易踩坑的行为,在 spec/api-shared-texture-spec.ts 中有专门测试覆盖:

  • 释放的幂等性:对已释放对象二次 release 必须是 no-op 而不是崩溃(retained.release() 调用两次,第二次断言不抛异常);
  • 释放后再使用的明确报错:释放后调用 getFrameCreationSyncToken() 等成员会抛出匹配 /released/ 的错误,便于定位“悬空使用”类 bug。

2.4 subtle:低级 API 出口

SharedTextureImported.subtle 与独立调用的 sharedTexture.subtle.importSharedTexture(textureInfo) 返回的是同类型对象,除 getVideoFrame/release 外还提供:

成员 作用
getVideoFrame 同上,创建 VideoFrame
release(callback?) 释放资源;callbackGPU 命令缓冲完成使用该纹理后触发,是安全释放依赖资源(如源纹理、主进程侧导入对象)的精确时机
startTransferSharedTexture 生成可序列化、可跨进程传输的 SharedTextureTransfer
getFrameCreationSyncToken 返回 SharedTextureSyncToken,通常与 setReleaseSyncToken 成对使用
setReleaseSyncToken(syncToken) 延迟本对象底层资源的释放,直到指定 sync token 在 GPU 进程被满足

受管与精细两套 API 的差异可概括为:受管 API 把生命周期记账(跨进程引用计数、allReferencesReleased 回调)封装在 JS 层;精细 API 把同一套语义拆成 release 回调 + SyncToken 原语,交给使用者手工编排。从 lib/renderer/api/shared-texture.ts 可见,sharedTexture.subtle 实际上直接暴露原生绑定 electron_common_shared_texture,而受管 release 只是在其 release 回调之上追加了一次回主进程的 IPC——这正是“subtle 是底层、managed 是封装”的实现证据。

三、受管 API 的跨进程传输细节

sendSharedTextureSharedTextureImported 唯一合法的跨进程分发方式(受管路径下),其约束值得逐条明确:

  • options.frame 必须是 WebFrameMain;目标是 WebContents 时传 webContents.mainFrame
  • 目标是非主 frame(如 iframe)时,必须为该 frame 启用 webPreferences.nodeIntegrationInSubFrames,因为该功能依赖主进程与目标 frame 之间的 frame IPC;
  • 调用前渲染进程必须已通过 setSharedTextureReceiver 注册接收回调,且渲染进程必须存活——该方法的超时时间为 1000ms
  • 返回 Promise<void>,传输完成时 resolve;...args 透传给渲染进程的接收回调。

渲染进程侧的内部流程(见 lib/renderer/api/shared-texture.ts):收到 IMPORT_SHARED_TEXTURE_TRANSFER_MAIN_TO_RENDERER 内部消息后,先调用原生 finishTransferSharedTexture 重建对象,再取 getFrameCreationSyncToken() 通过一次性应答通道回传给主进程(这一步确保主进程不会在 GPU 进程真正拿到引用前释放资源),最后才把包装后的 SharedTextureImported 交给用户回调。

传输载荷本身是 SharedTextureTransfer 对象,包含 transfer(不透明序列化数据)、syncToken(帧创建用的不透明 token 数据)、pixelFormatcodedSizevisibleRecttimestamp 六个只读属性。而导入侧的输入结构 SharedTextureImportTextureInfo 则定义了纹理描述:

  • pixelFormatbgra / rgba(32bpp)、rgbaf16(半浮点)、nv12 / nv16 / p010le(YUV 系列);
  • colorSpace(可选)、codedSize(纹理完整尺寸)、visibleRect(默认为整个 codedSize 区域)、timestamp(微秒,会反映到 VideoFrame);
  • handleSharedTextureHandle,平台相关句柄。

四、平台句柄与所有权:导入前的硬约束

设计文档(基于 Electron 37 / Chromium 137 撰写)说明了导入路径的底层选型:以 Chromium 的 SharedImage 作为外部纹理的底层承载,因为 SharedImageInterface::CreateSharedImage 接受包含各平台原生共享句柄的 GpuMemoryBufferHandle

  • Windows:NT HANDLE 形式的共享 D3D11 纹理(CreateSharedHandle 生成)。注意旧的 GetSharedHandle 产生的是非 NT HANDLE——不可用于导入,因为 Chromium 取得 GpuMemoryBuffer 所有权后会在销毁时调用 CloseHandle,而对非 NT HANDLE 执行该操作是非法操作并会导致崩溃。因此 importSharedTexture 内部会对 NT HANDLE 做一次 DuplicateHandle
  • macOSIOSurfaceRefIOSurface 是引用计数资源,导入时 Chromium 只增加引用而不接管所有权,因此比 Windows 简单;
  • LinuxNativePixmapHandle,每平面一个文件描述符。

文档同时给出一个必须满足的前置条件:调用 importSharedTexture 时,句柄必须已经对当前进程可见。跨进程传递句柄本身的脏活(Windows 的 DuplicateHandle、macOS 经 IPC 传 mach_port)需要调用方自行完成——这一点与 OSR 的 paint 事件不同,Chromium 的 IPC 在那里透明处理了句柄复制,所以 OSR 纹理可以直接用。

五、跨进程传输与 GPU 生命周期:Mailbox 与 SyncToken

两个进程各自持有引用同一 GPU 资源的 SharedImage 时,核心难题是“不知道 GPU 何时用完它”。设计文档给出的解法:

  1. 传输复用 Mojo 序列化SharedImage 持有指向 GPU 进程中 SharedImageBackingMailbox 引用;SharedImageInterface->ImportSharedImage + ClientSharedImage->Export 可以把足够的信息序列化,使另一个进程能取回同一个 SharedImage,并可复用 mojo serializer 序列化为字符串——这正是 SharedTextureTransfer.transfer 字段是不透明字符串的原因。

  2. 释放时序靠 SyncToken 保证SharedImage 的销毁会携带销毁 token。若纹理已被导入 WebGPU 管线,销毁 token 由 WebGPU 生成,确保 GPU 渲染完成前不会销毁资源。实现层面通过 gpu::ContextSupport 注册回调监听特定 SyncToken 被 signal 的时刻——这就是 release(callback) 回调与 setReleaseSyncToken 的机制来源:

    • release(callback):若该对象产生的 VideoFrame 已进入 WebGPU 管线,release等待 WebGPU 渲染完成后触发 callback,此时才可以安全释放依赖资源(主进程侧的导入对象、源纹理等);
    • setReleaseSyncToken(syncToken):让资源“不等到 release 调用、而等到某个 sync token 在 GPU 进程被满足”才释放。配合 getFrameCreationSyncToken(目标侧取 token → 传给源侧设置),使用者可以完全不依赖 release 回调来管理生命周期;
    • 典型顺序(来自 SharedTextureImportedSubtle 文档):目标进程先 finishTransferSharedTexture,再取 getFrameCreationSyncToken 回传,源进程对其 setReleaseSyncToken,防止源对象在 GPU 进程异步取到引用之前释放底层资源。

受管 API 把上述 token 交换封装掉了(渲染进程内部实现里那次一次性应答通道的 syncToken 回传即对应此步),这也是两套 API 的分工边界。

六、端到端验证:官方测试用例的完整链路

spec/api-shared-texture-spec.ts 是该功能的完整验证,以 OSR 内置纹理为源:new BrowserWindow({ webPreferences: { offscreen: { useSharedTexture: true } } })setFrameRate(1) 后监听 paint 事件拿到 event.texture。测试分两条路径:

精细 API 路径spec 第 68–101 行):主进程 subtle.importSharedTexture(texture.textureInfo)startTransferSharedTexture() 生成 SharedTextureTransfer → 经 webContents.send 传到渲染进程(fixture 位于 spec/fixtures/api/shared-texture/subtle/,渲染端 finishTransferSharedTexture 后走 WebGPU 渲染)→ 渲染完成后回发 shared-texture-done → 主进程 importedSubtle.release(callback)在 GPU 完成的回调里texture.release() 释放源纹理 → capturePage 截屏与目标图逐像素比对,容差为全图 1%(macOS 全链路渲染与 dpr 缩放可能引入微小色差)。

受管 API 路径importSharedTexture + sendSharedTexture + allReferencesReleased(见第二节示例代码),并额外覆盖 iframe 场景(nodeIntegrationInSubFrames: true,fixture 位于 spec/fixtures/api/shared-texture/managed/)。

适用前提需注意:该测试套件当前仅在 macOS arm64 上运行spec 第 13–15 行),且依赖 GPU 与 WebGPU 可用(测试在 texture 为空或 WebGPU 不可用时跳过)。这提示该实验性 API 的跨平台成熟度仍以仓库当前状态为准,非 darwin-arm64 平台的使用者应自行验证。

七、小结与使用约束清单

  • SharedTextureImported 是受管 API 的核心句柄:getVideoFrame 产出标准 VideoFramerelease 做跨进程引用计数释放,subtle 通向低级 SharedTextureImportedSubtle
  • 导入前置条件:句柄必须对当前进程可见(Windows 用 NT HANDLE、macOS 用 IOSurfaceRef、Linux 用 fd);调用 sendSharedTexture 前渲染进程必须注册 receiver 且存活,传输超时 1000ms;
  • 资源释放的正确顺序:VideoFrame.close()imported.release()(或 subtle 路径的 release 回调 / sync token)→ 所有进程引用归零后释放源纹理;
  • 全部相关 API 均为 Experimental,引用具体行为时以当前仓库文档(sharedTexture设计说明,后者注明基于 Electron 37 / Chromium 137)为准。
登录后查看全文
热门项目推荐
相关项目推荐