Electron SharedTextureImported 对象详解:跨进程共享纹理导入、VideoFrame 渲染与 GPU 资源生命周期管理
SharedTextureImported 是 Electron sharedTexture 模块(实验性 API)中“受管(managed)”导入接口的核心返回对象:它代表一个已被导入到当前进程、可供渲染使用的跨进程共享纹理。读完本文,你将理解该对象四个成员(textureId、getVideoFrame、release、subtle)的确切语义、它与 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 等 |
SharedTextureImported 由 importSharedTexture 在主进程中创建,其成员定义完整收录于 结构文档。它同时承担两个职责:一是把纹理以标准 VideoFrame 形式暴露给 Web 渲染栈;二是作为跨进程传输的载体(通过 sendSharedTexture 序列化到渲染进程,对端仍会得到一个 SharedTextureImported 实例)。
所有相关 API 均标注为 Experimental,官方明确提示“可能在未来被移除”,生产环境使用时需锁定 Electron 版本并保留回退路径。
二、对象成员逐一解析
2.1 textureId:导入纹理的全局标识
textureIdstring — 导入的共享纹理的唯一标识符。
该 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 消费纹理
getVideoFrameFunction<VideoFrame> — 在当前进程中创建一个使用已导入共享纹理的VideoFrame。使用完毕后可调用VideoFrame.close(),底层资源会在内部等待 GPU 完成(wait for GPU finish)。
这是把“裸句柄”变成 Web 标准对象的关键一步。VideoFrame 可以直接喂给 WebGPU 的 importExternalTexture/VideoFrame 导入路径,也可以用于 OffscreenCanvas 等场景。文档强调两点使用纪律:
- 必须调用
VideoFrame.close()。VideoFrame持有对纹理的引用,不关闭会造成纹理引用无法归零,最终使release永远无法完成; - GPU 完成等待是内建的。
getVideoFrame之后立即release是安全的——释放路径会经过SyncToken机制等待 GPU 命令缓冲执行完毕(见第五节设计原理)。
2.3 release():引用计数式释放
releaseFunction — 释放本对象对该共享纹理的引用。底层资源在所有引用释放前会一直存活。
“所有引用”是跨进程维度的。受管 API 下,主进程 importSharedTexture 的 options.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 出口
subtleSharedTextureImportedSubtle — 为高级用户提供的精细控制接口。
SharedTextureImported.subtle 与独立调用的 sharedTexture.subtle.importSharedTexture(textureInfo) 返回的是同类型对象,除 getVideoFrame/release 外还提供:
| 成员 | 作用 |
|---|---|
getVideoFrame |
同上,创建 VideoFrame |
release(callback?) |
释放资源;callback 在 GPU 命令缓冲完成使用该纹理后触发,是安全释放依赖资源(如源纹理、主进程侧导入对象)的精确时机 |
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 的跨进程传输细节
sendSharedTexture 是 SharedTextureImported 唯一合法的跨进程分发方式(受管路径下),其约束值得逐条明确:
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 数据)、pixelFormat、codedSize、visibleRect、timestamp 六个只读属性。而导入侧的输入结构 SharedTextureImportTextureInfo 则定义了纹理描述:
pixelFormat:bgra/rgba(32bpp)、rgbaf16(半浮点)、nv12/nv16/p010le(YUV 系列);colorSpace(可选)、codedSize(纹理完整尺寸)、visibleRect(默认为整个 codedSize 区域)、timestamp(微秒,会反映到VideoFrame);handle:SharedTextureHandle,平台相关句柄。
四、平台句柄与所有权:导入前的硬约束
设计文档(基于 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; - macOS:
IOSurfaceRef。IOSurface是引用计数资源,导入时 Chromium 只增加引用而不接管所有权,因此比 Windows 简单; - Linux:
NativePixmapHandle,每平面一个文件描述符。
文档同时给出一个必须满足的前置条件:调用 importSharedTexture 时,句柄必须已经对当前进程可见。跨进程传递句柄本身的脏活(Windows 的 DuplicateHandle、macOS 经 IPC 传 mach_port)需要调用方自行完成——这一点与 OSR 的 paint 事件不同,Chromium 的 IPC 在那里透明处理了句柄复制,所以 OSR 纹理可以直接用。
五、跨进程传输与 GPU 生命周期:Mailbox 与 SyncToken
两个进程各自持有引用同一 GPU 资源的 SharedImage 时,核心难题是“不知道 GPU 何时用完它”。设计文档给出的解法:
-
传输复用 Mojo 序列化:
SharedImage持有指向 GPU 进程中SharedImageBacking的Mailbox引用;SharedImageInterface->ImportSharedImage+ClientSharedImage->Export可以把足够的信息序列化,使另一个进程能取回同一个SharedImage,并可复用 mojo serializer 序列化为字符串——这正是SharedTextureTransfer.transfer字段是不透明字符串的原因。 -
释放时序靠 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产出标准VideoFrame,release做跨进程引用计数释放,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)为准。
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 StartedRust0624
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