Skip to content

[WebGPU] Support binding GPUExternalTexture via importExternalTexture - #9936

Open
AdrianEddy wants to merge 2 commits into
gfx-rs:trunkfrom
AdrianEddy:webgpu-external-texture-binding
Open

[WebGPU] Support binding GPUExternalTexture via importExternalTexture#9936
AdrianEddy wants to merge 2 commits into
gfx-rs:trunkfrom
AdrianEddy:webgpu-external-texture-binding

Conversation

@AdrianEddy

@AdrianEddy AdrianEddy commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Description

Implements the GPUExternalTexture binding type on the WebGPU backend, which was previously unimplemented!().

A new Device::import_external_texture imports a GPUExternalTexture from a video source — an HTMLVideoElement or a WebCodecs VideoFrame — via GPUDevice.importExternalTexture. The browser performs the YCbCr-to-RGB conversion internally, so unlike the plane-based create_external_texture used by the native backends, no plane textures or conversion matrices are supplied. A texture_external binding can then sample the frame with no copy.

  • WebExternalTexture now holds the GpuExternalTexture handle.
  • The BindingResource::ExternalTexture bind-group arm sets the entry's resource to that handle.
  • ExternalTexture::destroy() and its Drop are no-ops: a GPUExternalTexture has no destroy() and expires automatically (a VideoFrame source stays valid until closed; an HTMLVideoElement source for the current task).
  • The plane-based Device::create_external_texture remains unsupported on this backend, since the browser builds external textures from a video source rather than plane textures.
  • ExternalTextureSource and GpuExternalTexture are re-exported under wgpu::webgpu.

Testing

Built for wasm32-unknown-unknown with --features webgpu.

Validated end-to-end in headless Chrome on both the SwiftShader CPU WebGPU adapter and Dawn on a discrete GPU: a WebCodecs VideoFrame is imported via Device::import_external_texture, sampled through a texture_external binding in a render pass on the caller's Device, and read back. The result reproduces the source's YCbCr matrix + range conversion exactly and matches a copyExternalImageToTexture reference of the same frame bit-for-bit. No automated test is added here, since the current wasm test harness has no way to synthesize a video source.

Squash or Rebase?

Squash

Checklist

  • I self-reviewed and fully understand this PR.
  • WebGPU implementations built with wgpu may be affected behaviorally.
  • Validation and feature gates are in place to confine behavioral changes.
  • Tests demonstrate the validation and altered logic works.
  • CHANGELOG.md entries for the user-facing effects of this change are present.
  • The PR is minimal, and doesn't make sense to land as multiple PRs.
  • Commits are logically scoped and individually reviewable.
  • The PR description has enough context to understand the motivation and solution implemented.

Implements the `GPUExternalTexture` binding type on the WebGPU backend,
which was previously `unimplemented!()`.

`Device::import_external_texture` imports a `GPUExternalTexture` from a video
source (an `HTMLVideoElement` or a WebCodecs `VideoFrame`) via
`GPUDevice.importExternalTexture`. The browser performs the YCbCr->RGB
conversion internally, so unlike the plane-based `create_external_texture`
used by the native backends, no plane textures or conversion matrices are
supplied; a `texture_external` binding can then sample the frame with no copy.

- `WebExternalTexture` now holds the `GpuExternalTexture` handle.
- The `BindingResource::ExternalTexture` bind-group arm sets the entry's
  resource to that handle.
- `ExternalTexture::destroy()` and its `Drop` are no-ops: a
  `GPUExternalTexture` has no `destroy()` and expires automatically.
- The plane-based `create_external_texture` stays unsupported on this backend,
  since the browser builds external textures from a video source, not planes.
- `ExternalTextureSource` and `GpuExternalTexture` are re-exported under
  `wgpu::webgpu`.
@AdrianEddy
AdrianEddy force-pushed the webgpu-external-texture-binding branch from 335b0fb to e723d5a Compare July 23, 2026 16:04
Comment thread wgpu/src/backend/webgpu.rs Outdated
Comment thread wgpu/src/backend/webgpu.rs Outdated
- Move `ExternalTextureSource` from beside `DropCallback` down next to
  `WebExternalTexture`, with the rest of the external-texture code.
- Build the bind-group entry with the generated
  `GpuBindGroupEntry::new_with_gpu_external_texture` instead of hand-rolling
  an `Object` cast plus the two setters.
@AdrianEddy
AdrianEddy force-pushed the webgpu-external-texture-binding branch from 256a5c9 to d6ba736 Compare July 26, 2026 12:12

@inner-daemons inner-daemons left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Overall LGTM, just a few notes. Also, is it feasible for this to be tested in CI?

Comment thread wgpu/src/api/device.rs
&self,
source: &webgpu::ExternalTextureSource,
) -> ExternalTexture {
let inner = self.inner.as_webgpu().import_external_texture(source);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I feel like we might want this to return a result so that calling it from a non-webgpu backend doesn't panic.

Comment thread wgpu/src/api/device.rs
/// a no-op.
#[cfg(webgpu)]
#[must_use]
pub fn import_external_texture(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We also have create_external_texture, I think both of these warrant some documentation on how they differ and what their use case is.

@inner-daemons inner-daemons self-assigned this Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants