尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Electron SharedTextureTransfer 对象详解:共享纹理跨进程传输的机制与实战

Electron SharedTextureTransfer 对象详解:共享纹理跨进程传输的机制与实战 Electron SharedTextureTransfer 对象详解共享纹理跨进程传输的机制与实战【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electronSharedTextureTransfer是 ElectronsharedTexture模块中用于在浏览器进程与渲染进程之间传输共享纹理的核心数据结构它把一个已在某进程导入的SharedImage序列化为纯数据对象使纹理引用可以像普通 IPC 消息一样被传递。读懂这个对象的六个字段及其底层生成/消费流程后你能够基于 OSR离屏渲染产出的纹理句柄将外部共享纹理导入 Electron、跨进程转移到渲染进程并用VideoFrame WebGPU 完成零拷贝渲染与安全的资源释放。SharedTextureTransfer 在 sharedTexture API 体系中的位置sharedTexture模块负责把平台特定的纹理句柄Windows 的 NT HANDLE、macOS 的IOSurfaceRef、Linux 的NativePixmapHandle导入 Electron 并转换为标准的 WebVideoFrame详见 sharedTexture 模块文档。整个跨进程流转涉及四类对象对象作用文档SharedTextureSubtle底层细粒度 APIimportSharedTexture、finishTransferSharedTextureshared-texture-subtle.mdSharedTextureImportedSubtle已导入纹理的句柄对象getVideoFrame、release、startTransferSharedTexture、getFrameCreationSyncToken、setReleaseSyncTokenshared-texture-imported-subtle.mdSharedTextureTransfer本文主题可序列化、可跨进程传输的纹理描述数据shared-texture-transfer.mdSharedTextureSyncToken不透明同步令牌数据用于 GPU 侧的资源生命周期同步shared-texture-sync-token.md典型链路为主进程从 OSRpaint事件offscreen: { useSharedTexture: true }或其他原生渠道获得纹理句柄调用sharedTexture.subtle.importSharedTexture(textureInfo)得到SharedTextureImportedSubtle调用其startTransferSharedTexture()得到SharedTextureTransfer通过 IPCwebContents.send或托管 APIsendSharedTexture把该对象送入渲染进程渲染进程调用sharedTexture.subtle.finishTransferSharedTexture(transfer)取回一个新的SharedTextureImportedSubtle之后用getVideoFrame()参与渲染。shared-texture-transfer.md 文档的最后一句正点明了第 5 步“UsesharedTexture.subtle.finishTransferSharedTextureto getSharedTextureImportedSubtleback.”字段逐项解析六个 Readonly 属性SharedTextureTransfer的全部属性均为_Readonly_在 C 侧通过SetReadOnly绑定见 electron_api_shared_texture.cc#L284-L293生成后不可修改字段类型文档说明源码层面的实际含义transferstring共享纹理的不透明传输数据可跨 Electron 进程传递gpu::ClientSharedImage::Export()得到的gpu::ExportedSharedImage经 mojo 序列化器编码后再 Base64 的字符串承载了指向 GPU 进程中SharedImageBacking的Mailbox引用syncTokenstring帧创建用的不透明同步令牌数据源对象frame_creation_sync_token的 Base64 编码gpu::SyncToken原始字节流见 GetBase64StringFromSyncToken接收端用于WaitSyncToken保证目标对象在 GPU 侧真正取得资源之前源对象不会释放它pixelFormatstring正在传输的纹理的像素格式由TransferVideoPixelFormatToString映射见下文取值表codedSizeSize共享纹理的完整尺寸gfx::Size coded_size即帧数据完整维度visibleRectRectangle[0, 0, codedSize.width(), codedSize.height()]内的子区域常见情况下就是全帧gfx::Rect visible_rectOSR 场景下默认取整个 codedSize导入转换时若未提供即gfx::Rect(coded_size)见Converter ::FromV8timestampnumber微秒级时间戳会被反映到VideoFrameint64_t timestamp自采集开始起算的微秒数创建VideoFrame时以base::Microseconds(timestamp)传入 media::VideoFrame::WrapSharedImagepixelFormat字符串与内部枚举的双向映射定义在 TransferVideoPixelFormatToString 及导入时的ConverterImportSharedTextureInfo::FromV8字符串内部media::VideoPixelFormatbgraPIXEL_FORMAT_ARGBrgbaPIXEL_FORMAT_ABGRrgbaf16PIXEL_FORMAT_RGBAF16nv12PIXEL_FORMAT_NV12nv16PIXEL_FORMAT_NV16p010lePIXEL_FORMAT_P010LE注意一个反直觉细节ARGB在传输对象上表现为bgraABGR表现为rgba。此外getVideoFrame()只能用于渲染进程——源码中浏览器进程调用会直接抛出 “The VideoFrame cannot be created at current process.”ImportedTextureGetVideoFrame所以“主进程生成 Transfer、渲染进程消费 Transfer”正是被设计支持的方向。生成端startTransferSharedTexture 的序列化实现SharedTextureTransfer由SharedTextureImportedSubtle.startTransferSharedTexture()创建C 实现是 ImportedSharedTexture::StartTransferSharedTexturev8::Localv8::Value ImportedSharedTexture::StartTransferSharedTexture( v8::Isolate* isolate) { auto exported client_shared_image-Export(); // Use mojo to serialize the exported shared image. mojo::Message message(0, 0, MOJO_CREATE_MESSAGE_FLAG_UNLIMITED_SIZE, 0); mojo::internal::MessageFragment gpu::mojom::internal::ExportedSharedImage_Data data(message); data.Allocate(); mojo::internal::Serializergpu::mojom::ExportedSharedImageDataView, gpu::ExportedSharedImage::Serialize(exported, data); auto encoded base::Base64Encode(...message payload...); gin_helper::Dictionary root(isolate, v8::Object::New(isolate)); root.SetReadOnly(transfer, encoded); root.SetReadOnly(syncToken, GetBase64StringFromSyncToken(frame_creation_sync_token)); root.SetReadOnly(pixelFormat, TransferVideoPixelFormatToString(pixel_format)); root.SetReadOnly(codedSize, coded_size); root.SetReadOnly(visibleRect, visible_rect); root.SetReadOnly(timestamp, timestamp); return gin::ConvertToV8(isolate, root); }从实现可以看出三点关键事实传输的是 GPU 进程的引用而不是像素数据。ClientSharedImage::Export()导出的是ExportedSharedImage其核心是Mailbox——指向 GPU 进程中同一块SharedImageBacking的引用。这与 shared_texture 设计文档 的设计说明一致正是SharedImage对Mailbox的持有使得跨进程共享成为可能该设计文档注明基于 Electron 37 / Chromium 137 编写且提示 Chromium 的SharedImage接口尤其是GpuMemoryBuffer、VideoFrame生命周期管理部分可能快速演进。transfer是 Base64 字符串因此可被任意 IPC 机制透传。mojo 消息体被编码为字符串后就能走webContents.send、ipcRenderer.invoke等结构化克隆通道这正是“can be transferred across Electron processes”的由来。调用前提对象未被release()且当前进程 GPU 可用否则分别抛出 “The shared texture has been released.” 或 “Failed to start shared texture transfer: GPU is not available”ImportedTextureStartTransferSharedTexture。消费端finishTransferSharedTexture 的反序列化与同步保证接收侧的 C 实现是 FinishTransferSharedTexture与生成端严格对称v8::Localv8::Value FinishTransferSharedTexture(v8::Isolate* isolate, v8::Localv8::Value options) { // ... 从 options 中取出 id / transfer / syncToken 三个字符串 ... auto transfer_data base::Base64Decode(transfer); // Use mojo to deserialize the exported shared image. mojo::Message message(transfer_data.value(), {}); // ... Claim Deserialize 出 gpu::ExportedSharedImage ... auto* sii GetSharedImageInterface(); // 无 GPU 时抛错 auto si sii-ImportSharedImage(std::move(exported)); auto source_st GetSyncTokenFromBase64String(sync_token_data); sii-WaitSyncToken(source_st); // 等待源对象的帧创建同步令牌 ImportedSharedTexture* imported new ImportedSharedTexture(); imported-pixel_format partial.pixel_format; imported-codedSize / visibleRect / timestamp 从传输对象恢复; imported-frame_creation_sync_token sii-GenUnverifiedSyncToken(); imported-client_shared_image std::move(si); imported-id id; return CreateImportedSharedTextureFromSharedImage(isolate, imported); }其中WaitSyncToken(source_st)是跨进程生命周期的关键一环目标对象在 GPU 侧真正“取得”纹理引用之前源对象侧通过gpu::SharedImageInterface的同步令牌机制不会销毁底层资源。源码注释electron_api_shared_texture.h 对应实现区明确了两种生命周期管理方式回调方式默认对目标对象调用release(callback)GPU 命令缓冲用完后回调触发再由用户去释放源对象——简单直观同步令牌方式进阶目标对象拿到getFrameCreationSyncToken()后传给调用过startTransferSharedTexture的源对象并执行setReleaseSyncToken此后源对象可直接release()无需关心目标进程是否完成异步获取。finishTransferSharedTexture接收的参数就是整个SharedTextureTransfer对象其字段名transfer、syncToken被逐一读取。绑定入口在 InitializeimportSharedTexture与finishTransferSharedTexture都注册在 Node 绑定electron_common_shared_texturenode_bindings.cc#L113下对应 JS 侧的sharedTexture.subtle。实战一subtle 手动模式主进程导入 → IPC 传 Transfer → 渲染进程消费以下流程改编自仓库内测试 spec/api-shared-texture-spec.ts 中 “successfully imported and rendered with subtle api” 用例该用例从 OSR 窗口捕获paint事件最终用capturePage与目标图像做像素级比对验证整条链路主进程const { BrowserWindow, sharedTexture, ipcMain } require(electron); const capturedTextures new Map(); // id - { importedSubtle, texture } const win new BrowserWindow({ width: 256, height: 256, webPreferences: { preload: preloadPath } }); // OSR 窗口useSharedTexture 让 paint 事件直接携带纹理句柄 const osr new BrowserWindow({ width: 128, height: 128, webPreferences: { offscreen: { useSharedTexture: true } } }); osr.webContents.setFrameRate(1); osr.webContents.on(paint, (event) { const texture event.texture; if (!texture) return; // GPU 可能不可用 // Step 2: 把 OSR 纹理句柄导入为 SharedTextureImportedSubtle const importedSubtle sharedTexture.subtle.importSharedTexture(texture.textureInfo); // Step 3: 生成可跨进程传递的 SharedTextureTransfer const transfer importedSubtle.startTransferSharedTexture(); const id randomUUID(); capturedTextures.set(id, { importedSubtle, texture }); // Step 4: 通过 IPC 把 transfer 对象纯数据发给渲染进程 win.webContents.send(shared-texture, id, transfer); }); ipcMain.on(shared-texture-done, (event, id) { // Step 12-14: 渲染进程释放完成后主进程再释放导入对象与源纹理 const { importedSubtle, texture } capturedTextures.get(id); capturedTextures.delete(id); importedSubtle.release(() texture.release()); });渲染进程 preload参考 spec/fixtures/api/shared-texture/subtle/preload.jsconst { sharedTexture } require(electron); const { ipcRenderer, contextBridge } require(electron/renderer); contextBridge.exposeInMainWorld(textures, { onSharedTexture: (cb) { ipcRenderer.on(shared-texture, async (e, id, transfer) { // Step 5: 用 SharedTextureTransfer 完成传输取回 SharedTextureImportedSubtle const importedSubtle sharedTexture.subtle.finishTransferSharedTexture(transfer); // Step 6: 交给 WebGPU 渲染getVideoFrame() 只能在渲染进程调用 await cb(id, importedSubtle); // Step 10: 用 release 的回调精确等待 GPU 命令缓冲结束 importedSubtle.release(() { ipcRenderer.send(shared-texture-done, id); }); }); } });几个值得注意的约束均有仓库依据纹理句柄必须对调用进程可见。设计文档明确Windows 上必须是CreateSharedHandle产生的 NT HANDLE非 NT 的旧式 HANDLE 会被 Chromium 的CloseHandle清理逻辑破坏导致崩溃macOS 上IOSurface需通过mach_port跨进程传递因此调用importSharedTexture前句柄须已可用ImportSharedTextureInfo 的平台注释。release()是手动义务若对象被 GC 而未调用release()弱引用回调会打出 “The imported shared texture ... was garbage collected before callingrelease(). You have to manually release the resource once youre done with it.” 的 ERROR 日志PersistentCallbackPass1。如果一次传输产生了多个SharedTextureImported对象比如同一纹理转发给多个进程每一个都必须releaseGPU 侧资源在最后一个被释放时才销毁SharedTextureImportedSubtle 文档。实战二managed 托管模式sendSharedTexture setSharedTextureReceiversharedTexture模块还封装了托管流程免去手动 IPC渲染进程先注册sharedTexture.setSharedTextureReceiver(callback)主进程调用sharedTexture.sendSharedTexture({ frame: webContents.mainFrame, importedSharedTexture }, ...args)该方法有 1000ms 超时要求接收端已就绪、渲染进程存活sharedTexture 模块文档。渲染进程侧的实现见 lib/renderer/api/shared-texture.tsipcRendererInternal.on(transferChannelName, async (event, requestId, ...args) { const replyChannel ${transferChannelName}_RESPONSE_${requestId}; const transfer args[0] as Electron.SharedTextureTransfer; const textureId args[1] as string; // 1) 在渲染进程完成 finishTransferSharedTexture const imported sharedTextureNative.finishTransferSharedTexture(Object.assign(transfer, { id: textureId })); // 2) 把 getFrameCreationSyncToken() 回传给主进程 // 供源对象 setReleaseSyncToken 使用对应上文“同步令牌方式” event.sender.send(replyChannel, null, imported.getFrameCreationSyncToken()); // 3) 组装 SharedTextureImported 包装对象并回调用户 await sharedTextureReceiverCallback?.(data, ...args.slice(2)); });对应测试 spec/fixtures/api/shared-texture/managed/preload.js 中release()无需回调——注释说明“util function automatically managed the lifetime via sync token”即托管模式已经替你走完了setReleaseSyncToken路径。渲染端用法managed/renderer.js非常简洁window.textures.onSharedTexture(async (imported) { const frame imported.getVideoFrame(); // Step 6: 取 VideoFrame await window.renderFrame(frame); // Step 7: WebGPU 渲染 frame.close(); // Step 8: 用完即关 imported.release(); });设计原理小结为什么 Transfer 是字符串而非句柄直传shared_texture 设计文档 解释了这条链路背后的四个工程障碍其中两个直接决定了SharedTextureTransfer的形态共享句柄是进程局部的Windows NT HANDLE 需要DuplicateHandle才能跨进程macOS 的IOSurface需要经mach_port传递。SharedTextureTransfer绕开了原生句柄传递因为transfer字段里装的是 GPU 进程侧SharedImageBacking的引用序列化数据——任何拿到它的进程都通过SharedImageInterface::ImportSharedImage在 GPU 进程中“认领”同一份 backing无需操作平台句柄。GPU 使用何时结束无法同步得知两个进程各持有一份引用同一Mailbox的SharedImage谁都不能先释放。这就是syncToken字段与release回调 /setReleaseSyncToken机制存在的意义——底层通过gpu::ContextSupport::SignalSyncToken在 GPU 命令缓冲真正完成后触发 JS 回调SetupReleaseSyncTokenCallback。适用前提与限制实验性 APIsharedTexture模块全部方法在文档中标记Experimental未来可能被移除shared-texture.md。GPU 必须可用startTransferSharedTexture/finishTransferSharedTexture/getFrameCreationSyncToken/setReleaseSyncToken在无 GPU 上下文时都会抛出 “GPU is not available” 错误测试用例也因此在捕获不到纹理时跳过比对。测试平台基线当前仓库的端到端测试 api-shared-texture-spec.ts 仅在 macOS arm64 上运行process.platform ! darwin || process.arch ! arm64时整体ifdescribe跳过设计文档亦声明其基于 Electron 37 / Chromium 137 编写且 ChromiumSharedImage相关接口演进较快其他平台的细节以实际仓库源码为准。进程分工importSharedTexture与startTransferSharedTexture可在主进程调用getVideoFrame()只能在渲染进程调用。若要导入渲染进程侧加载的原生代码产出的纹理需自行保证句柄对该进程可见设计文档 “Example” 一节。相关仓库资料索引文档SharedTextureTransfer、SharedTextureSubtle、SharedTextureImportedSubtle、SharedTextureSyncToken、sharedTexture 模块设计与实现shared_texture 设计文档、electron_api_shared_texture.cc、electron_api_shared_texture.h、lib/renderer/api/shared-texture.ts测试与夹具api-shared-texture-spec.ts、subtle/preload.js、managed/preload.js、managed/renderer.js【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表