Native Extensions
Native interop extensions to GPUDevice and the WebGPU globals.
React Native WebGPU adds a handful of native extensions on top of the WebGPU spec.
importSharedTextureMemory
Imports a native buffer (IOSurface / AHardwareBuffer) for zero-copy sampling.
Nothing has to be requested: requestDevice() enables the Dawn features behind native frame import whenever the adapter supports them (see Native textures).
const = await .();
// frame from createVideoPlayer(..., "bgra8") or Vision Camera
const = .({
: .,
: "camera-frame",
});
const = .();
.(, /* initialized */ true);
// sample texture in render pass…
.();
.();
.();Use beginAccess(texture, true) when the producer has already written pixels (typical for video/camera).
importSharedFence
Import native GPU sync fences for producer/consumer handoff:
// Producer side: export a fence after GPU work
const exported = sharedFence.export();
// Consumer side: import on another device or queue
const imported = device.importSharedFence({
handle: exported.handle,
label: "frame-ready",
});
// Wait for producer before sampling imported texture
imported.wait(0);
// … render using imported texture …
imported.signal(0);
imported.destroy();See packages/webgpu/src/__tests__/SharedFence.spec.ts for a full producer/consumer test.
dawnToggles
Non-standard Dawn device toggles at creation:
const = await .({
: {
: ["allow_unsafe_apis"],
: ["use_dxc"],
},
});Prop
Type
implicitDeviceSynchronization
Dawn's implicit-device-synchronization feature is requested by default by requestDevice(), so a device created on the JS thread can be used from the UI thread or a dedicated worklet runtime (see Threading model). Dawn wraps the device and child-object entry points in a per-device mutex; command encoding is not covered, so a GPUCommandEncoder must stay on the thread that created it.
Set implicitDeviceSynchronization: false on the device descriptor to opt out and skip the per-call locking. Only do this for a device that is never touched from another runtime. The flag is a non-standard extension and is ignored on the web.
const = await .({
: false,
});
..("implicit-device-synchronization" as ); // falseThe flag only controls the default injection: listing "implicit-device-synchronization" in requiredFeatures still enables the feature.
GPUExternalTexture extensions
| Field / Method | Description |
|---|---|
source | A NativeVideoFrame (from a video player, a camera buffer or createTestVideoFrame) instead of the web's HTMLVideoElement / VideoFrame |
rotation | 0, 90, 180, or 270 degrees (camera orientation) |
mirrored | Horizontal flip for front camera |
destroy() | Release native surface access after the queue.submit() that sampled it |
Usage with importExternalTexture (NV12 camera/video):
const externalTexture = device.importExternalTexture({
source: nativeVideoFrame,
});
// Read orientation metadata in bind group / shader setup
console.log(externalTexture.rotation, externalTexture.mirrored);
// After submit that sampled the texture:
externalTexture.destroy();
nativeVideoFrame.release();On Android, a YUV frame (what the video decoder and the camera produce) needs the opaque-ycbcr-android-for-external-texture device feature. requestDevice() enables it whenever the adapter supports it; a device created by another library and wrapped with importDevice() has to request it itself, as react-native-skia's does. The same applies to copyExternalImageToTexture() with a native frame.
copyExternalImageToTexture with a native frame
queue.copyExternalImageToTexture() also accepts a NativeVideoFrame as its source, the way the web API accepts a VideoFrame.
The frame is rendered into the destination on the GPU through an external texture, so YUV frames come out as RGB, and the non-spec rotation and mirrored options of importExternalTexture apply.
The destination needs GPUTextureUsage.RENDER_ATTACHMENT and an rgba8unorm, bgra8unorm (or -srgb), rgba16float or rgb10a2unorm format; the frame can be released as soon as the call returns.
const = .({
: [., .],
: "rgba8unorm",
: . | .,
});
..(
{ : , },
{ },
[., .],
);
.();origin, flipY and the copy size work as on the web, in the upright frame: a frame rotated by 90 or 270 degrees has its width and height swapped.
This is the path to hand a video or camera frame to a library that samples regular textures, such as React Native Skia; a shader of your own can sample the frame directly with importExternalTexture() and skip the copy.
Prop
Type
createImageBitmap
createImageBitmap mirrors its Web counterpart and also accepts a Uint8Array or ArrayBuffer as input.
const = await ().(() => .());
const = await ();
..(
{ : },
{ },
{ : ., : . },
);
.();getPreferredHighBitDepthCanvasFormat
Returns the canvas texture format most likely to reach the display with more than 8 bits per channel on the current platform: "rgba16float" on iOS, "rgb10a2unorm" on Android.
Unlike getPreferredCanvasFormat(), this helper is not part of the WebGPU standard, so it is exported as a regular function instead of a navigator.gpu method.
const = ();
.({ , , : "opaque" });The values you write stay sRGB-encoded exactly like on an 8-bit canvas: this is about precision, not HDR (no extended range). See High bit depth for the platform details and banding comparison.