Skip to content

rfc: Import Shared Texture #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

reitowo
Copy link
Contributor

@reitowo reitowo commented May 30, 2025

This feature provides a way to import a native shared texture handle into Electron, specifically in the form of VideoFrame, which by nature supports several Web rendering systems including WebGPU, WebGL. This enables developers to integrate arbitrary native rendered content with their web applications.

@reitowo reitowo force-pushed the main-shared-texture branch from 0c30774 to 05b8645 Compare May 31, 2025 07:36
@samuelmaddock samuelmaddock added the pending-review Waiting for reviewers to give feedback on the PR specifications label Jun 5, 2025
const transfer = imported.startTransferSharedTexture();

const id = randomUUID();
capturedTextures.set(id, { imported, texture });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any use cases for keeping the imported texture in the main process after initiating the transfer to the renderer?

I wonder if the API could be simplified where imported would become unusable in the main process if sent over IPC. This would be similar to transferrable objects. It'd also be valid to limit the imported shared texture to only be usable with MessagePort which accepts transferrable objects.

Copy link
Contributor Author

@reitowo reitowo Jun 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. It may be transferred to multiple renderer processes. For example, display this texture in multiple pages. The word "transfer" here does NOT mean handling over ownership like "transferrable objects" means. Considering this, we can not use MessagePort or anything that transfer ownership.
  2. We must wait the receiver to call finishTransferSharedTexture, before call release at sender side. More precisely, we have to hold reference until the GPU actually executed the import task for receiver. That's when it adds reference count to the underlying SharedImage & Mailbox. If we call release here right after startTransferSharedTexture, we submit a release task to GPU, and this task may be executed before the import task, which makes the reference count returning 0, and the SharedImage & Mailbox get finally destroyed, causing the import task fail.
  3. The IPC to notify main process that is OK to release is unavoidable.

});
}
});
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could handling the release of these textures be handled internally? I can think of two different ways to implement this:

  1. Collect capturedTextures within lib/ and use an internal IPC to release these.
  2. When release() is called from the renderer, send an internal Mojo IPC to release the native data structures.

Copy link
Contributor Author

@reitowo reitowo Jun 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also considered this, and I found if we want to let user transfer same texture to (multiple) different processes, we eventually creates a cross process reference counting map, which is fun, because in fact, the same thing SharedImage is doing. It use Mailbox as a key to reference counting and holds the SharedImage data. https://source.chromium.org/chromium/chromium/src/+/main:gpu/command_buffer/service/shared_image/shared_image_manager.h;drc=e76cd1dd569db9198eb674102f00a718a752487d;l=32

But, SharedImageManager has a drawback that I mentioned in the above conversation, the reference counting add/sub is not executed until GPU process run that command buffer. If we're able to rebuild the wheel, we true may creates a "sync" version of reference counting, and yes, we don't have to let user pass a callback to release function and manually notify main process to release. The only problem is: we have to syncly add the reference count at the time "startTransferSharedTexture" and hope the receiver will catch that thing. The ownership is not taken until receiver receive the object. If somehow the texture is lost, and release never get called, the texture is never released

The procedure will be changed to:

  1. User import shared texture at main process, we create a record and add reference
  2. User start transfer, we add reference syncly.
  3. User release it at main process as no longer needed. The texture is not released as it still have a reference count.
  4. User use finish transfer at renderer process to get it back, we do nothing on counter (already added at sender side)
  5. User finished accessing it at renderer process, call release, and we decrease the reference.
  6. The counter is now 0 and we eventually free the SharedImage.

The problem is:

  1. If somehow a started transfer texture never being received by user, it never get released, the SharedImage is memory leaked.
  2. We also can't wait to add reference count at finish transfer side, which requires main process to hold until this happens, and renderer process notify main to release, which makes no difference than current design.
  3. Performance, don't know how long it takes to update the counter. If there's a OS level counter to use?
  4. Repeated design of the counter as Chromium internally does that, although deferred executed (which is not ideal.)
  5. We maybe have to listen to multiple GC callbacks to detect if user leaked the texture, or automatically release for user. As this is a cross process holder, and we are dealing with multiple GC environments.

Copy link
Contributor Author

@reitowo reitowo Jun 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frankly speaking, a native implementation of cross process reference counting is not simple. Let's say it is named CrossProcessCounterService, in order to do that natively, we have to create a host service in main process, when creating subprocesses we have to pass mojo remotes to new process and creates a ClientInterface for this in order to communicate with host. All of these implementation is for provide a cross process counter, seems a little bit complicated.

@erickzhao erickzhao added pending-changes Waiting for changes from the PR author based on feedback given and removed pending-review Waiting for reviewers to give feedback on the PR specifications labels Jul 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pending-changes Waiting for changes from the PR author based on feedback given
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants