HTML nel canvas WebGPU

Il rendering dell'HTML all'interno di un canvas WebGPU offre tutti i vantaggi dell'HTML in un canvas WebGL, come l'applicazione di effetti shader al DOM o l'integrazione di HTML interattivo in scene 3D. Tuttavia, WebGPU offre vantaggi significativi rispetto a WebGL.

Questa guida mostra come eseguire il rendering del codice HTML in un canvas WebGPU. Scopri di più su HTML in Canvas nella panoramica dell'API.

Il rendering dell'HTML in un canvas WebGPU richiede i seguenti passaggi:

  1. Prepara la tela. Imposta in modo dichiarativo gli attributi corretti per far sapere al browser che deve eseguire il rendering del codice HTML in un canvas. Questa configurazione è identica nei contesti 2D, WebGL e WebGPU. Scopri come configurare il canvas e come dichiarare gli elementi HTML da disegnare in modo indipendente.
  2. Applica l'HTML come texture:
    1. Pre-allocare lo spazio di archiviazione delle texture della GPU.
    2. Carica l'istantanea DOM come texture.
  3. Sincronizza il DOM e i pixel.

Disegna un elemento HTML come texture WebGPU

In WebGPU, gli elementi HTML vengono caricati sulla GPU come texture 2D utilizzando drawElementImageToTexture().

Pre-allocate texture storage

WebGPU richiede l'allocazione esplicita della memoria. Prima di copiare i pixel DOM in una texture, devi prenotare la VRAM della GPU utilizzando il metodo device.createTexture():

const context = canvas.getContext("webgpu");

// Pre-allocate the texture size as needed
// For example: HTML element CSS size × devicePixelRatio
const size = canvas.getElementImageDefaultSize(myElement);
const myElementWidth = Math.ceil(size.width);
const myElementHeight = Math.ceil(size.height);

// Pre-allocate the GPUTexture backing (run once during setup)
const myTexture = device.createTexture({
  size: {
    width: myElementWidth,
    height: myElementHeight,
    depthOrArrayLayers: 1, // optional (defaults to 1)
  },
  format: "rgba8unorm",
  usage:
    GPUTextureUsage.TEXTURE_BINDING |
    GPUTextureUsage.COPY_DST |
    GPUTextureUsage.RENDER_ATTACHMENT,
});

Disegna l'immagine dell'elemento HTML

In WebGPU, il metodo drawElementImageToTexture() disegna l'istantanea dell'elemento HTML. Il metodo accetta due oggetti dizionario: source e destination.

source definisce l'elemento DOM di origine da rasterizzare e le coordinate di ritaglio del sottorettangolo facoltative che ti consentono di copiare solo una parte dell'elemento nella texture anziché l'intero nodo DOM:

Proprietà Tipo Descrizione
source obbligatorio Element o ElementImage L'elemento DOM di destinazione (o l'istanza ElementImage pre-acquisita) da disegnare nella texture.
sourceX facoltativi (tutti e quattro o nessuno) float L'offset della coordinata X orizzontale (in pixel CSS) che definisce l'angolo in alto a sinistra della sezione del sottorettangolo di origine.
sourceY facoltativi (tutti e quattro o nessuno) float L'offset della coordinata Y verticale (in pixel CSS) che definisce l'angolo in alto a sinistra della sezione del sottorettangolo di origine.
sourceWidth facoltativo (tutti e quattro o nessuno) float La larghezza (in pixel CSS) del sottorettangolo di origine da ritagliare e rasterizzare.
sourceHeight facoltativi (tutti e quattro o nessuno) float L'altezza (in pixel CSS) del rettangolo secondario di origine da ritagliare e rasterizzare.

destination definisce la texture GPU di destinazione che riceve l'istantanea DOM sottoposta a rendering, insieme a offset di destinazione, livelli mip e dimensioni di copia facoltativi per posizionare i pixel in una regione specifica della texture:

Proprietà Tipo Descrizione
texture obbligatorio GPUTexture La texture WebGPU di destinazione preallocata che riceverà l'istantanea dei pixel del DOM sottoposto a rendering.
size facoltative GPUExtent3D La larghezza e l'altezza (in pixel della texture fisica, ad esempio { width, height }) per ridimensionare e rasterizzare l'elemento all'interno della texture di destinazione.
origin facoltative GPUOrigin3D L'offset di destinazione 3D (x, y, z) all'interno della texture in cui verranno scritti i pixel rasterizzati. Il valore predefinito è [0, 0, 0].
mipLevel facoltative GPUIntegerCoordinate Il livello di mipmap specifico della texture di destinazione in cui scrivere. Il valore predefinito è 0.
aspect facoltative GPUTextureAspect Specifica l'aspetto della texture in cui eseguire la copia ("all", "stencil-only" o "depth-only"). Il valore predefinito è "all".
colorSpace facoltative PredefinedColorSpace Lo spazio dei colori di destinazione (ad esempio "srgb", "display-p3") per la conversione del colore al momento del caricamento. Il valore predefinito è "srgb".
premultipliedAlpha facoltative booleano Indica se l'alpha deve essere premoltiplicato nei canali di colore durante il caricamento. Il valore predefinito è false.

Carica l'elemento nella texture all'interno dell'evento paint chiamando il metodo drawElementImageToTexture():

canvas.addEventListener("paint", (event) => {
  const sourceDict = {
    source: myElement,
    // Optional sub-rectangle crop in CSS pixels
    // (defaults to full element if omitted):
    sourceX: 0,
    sourceY: 0,
    sourceWidth: myElement.offsetWidth,
    sourceHeight: myElement.offsetHeight,
  };

  const destDict = {
    texture: myTexture,
    size: { width: myElementWidth, height: myElementHeight },
  };

  // Upload the latest DOM snapshot into the pre-allocated GPUTexture
  device.queue.drawElementImageToTexture(sourceDict, destDict);
});

Sincronizza la geometria

Per allineare l'hit test DOM e gli anelli di messa a fuoco dell'accessibilità con l'output del canvas sottoposto a rendering, crea una trasformazione DOMMatrix e applicala con il metodo updateElementGeometry():

// Construct a transform as needed in CSS pixels
// relative to the canvas, for example:
// 1/8 of the canvas width from the left edge, vertically centered.
// scaleX, scaleY: canvas grid pixels per CSS pixel (from resize observer)
const canvasCssWidth = canvas.width / scaleX;
const canvasCssHeight = canvas.height / scaleY;
const elementCssHeight = canvas.getElementImageDefaultSize(myElement).height / scaleY;
const x = canvasCssWidth / 8;
const y = (canvasCssHeight - elementCssHeight) / 2;
const transform = new DOMMatrix().translate(x, y);

// Synchronize the element's DOM hit-testing and accessibility geometry
canvas.updateElementGeometry(myElement, { canvasTransform: transform });

Passaggi successivi