React Native WebGPU

drawElementImageToTexture

Copy the pixels of a native React Native view into a GPUTexture.

queue.drawElementImageToTexture(source, destination) rasterizes a native React Native view (the "element") and writes the result into a GPUTexture. It follows the shape of the html-in-canvas proposal for WebGPU, where the element is a DOM node; here it is any mounted React Native view.

const  = .();
const  = .({
  : [.(320 * ), .(200 * )],
  : "rgba8unorm",
  : . | .,
});

// The view is rasterized on the UI thread, so the call is asynchronous.
await ..(
  { :  },
  {  },
);
// Work submitted from here on samples the new contents.

The view must stay in the native hierarchy: render it with collapsable={false} so React Native does not optimize the host view away.

<View ref={cardRef} collapsable={false} style={{ width: 320, height: 200 }}>
  {/* any React Native content */}
</View>

Source

interface GPUDrawElementImageSource {
  source: View ref | host instance | number;
  sourceX?: number;
  sourceY?: number;
  sourceWidth?: number;
  sourceHeight?: number;
  afterScreenUpdates?: boolean;
}
  • source is a host component ref (the useRef object), the host instance itself (ref.current), or the tag returned by findNodeHandle. Worklet runtimes have no findNodeHandle; pass the instance or the tag there.
  • The optional rectangle selects the part of the view to draw. It is expressed in the view's own coordinate space, in layout points (density-independent), and defaults to the whole view.
  • afterScreenUpdates (iOS only, default true) flushes pending layout and layer changes before drawing, so a snapshot taken right after a state change reflects it. Pass false to draw the last committed frame instead, which is cheaper when capturing repeatedly. Android renders the views' current display lists either way.

Destination

interface GPUDrawElementImageDestination extends GPUCopyExternalImageDestInfo {
  size?: GPUExtent3D;
}
  • texture must be rgba8unorm, bgra8unorm or one of their -srgb variants, with GPUTextureUsage.COPY_DST. mipLevel, origin and aspect work as in writeTexture.
  • Add GPUTextureUsage.RENDER_ATTACHMENT to let the view be drawn into the texture on the GPU (iOS, and Android 13 and above, see below). Without it, or on older Android versions and macOS, the pixels are read back through the CPU and written with writeTexture.
  • size is the pixel size of the region the view is drawn into, and the view is scaled to fit it. It defaults to the natural pixel size of the source rectangle: its size in points times the device pixel ratio.
  • premultipliedAlpha defaults to false, as in copyExternalImageToTexture: the (premultiplied) snapshot is converted to straight alpha unless you ask for premultiplied pixels.

The call rejects when the region does not fit in the texture, when the view is not mounted or has no size, or when the texture format is unsupported.

What gets captured

The snapshot is what the platform renders for that view hierarchy:

  • iOS renders through drawViewHierarchyInRect, which includes Metal layers such as a WebGPU Canvas. With RENDER_ATTACHMENT, the pixels reach the texture without a copy through the CPU; otherwise they are drawn into a bitmap and uploaded.
  • Android 10 (API 29) and above renders the views with the same renderer as the screen, so the result matches it pixel for pixel, including elevation shadows and TextureView content. Older versions draw the view into a software canvas, which misses content that is only drawn hardware-accelerated.
  • Android 13 and above, with RENDER_ATTACHMENT: the pixels reach the texture without a copy through the CPU, and come out the same.
  • On Android, SurfaceView content is composed outside the view hierarchy and comes out blank. A WebGPU Canvas with android={{ backingView: "SurfaceView" }} is not captured: keep the default TextureView for a canvas that must appear in a snapshot.

Threading

Unlike the web API, the method returns a Promise. The view can only be rasterized on the platform UI thread, so the call hops there and back. The texture is written on the calling runtime's thread right before the promise resolves, so command buffers submitted after await observe the new pixels. Calls made from a worklet runtime settle on that runtime.