HTML dans le canevas WebGPU

Le rendu HTML dans un canevas WebGPU offre tous les avantages du HTML dans un canevas WebGL, comme l'application d'effets de nuanceur au DOM ou l'intégration de HTML interactif dans des scènes 3D. Toutefois, WebGPU offre des avantages considérables par rapport à WebGL.

Ce guide explique comment afficher du code HTML dans un canevas WebGPU. En savoir plus sur le HTML dans Canvas dans la présentation de l'API

Pour afficher du code HTML dans un canevas WebGPU, procédez comme suit :

  1. Préparez votre toile. Définissez de manière déclarative les attributs appropriés pour indiquer au navigateur qu'il doit afficher du code HTML dans un canevas. Cette configuration est identique pour les contextes 2D, WebGL et WebGPU. Découvrez comment configurer votre canevas et comment déclarer des éléments HTML à dessiner indépendamment.
  2. Appliquez le code HTML en tant que texture :
    1. Préallouer l'espace de stockage des textures GPU.
    2. Importez l'instantané DOM en tant que texture.
  3. Synchronisez le DOM et les pixels.

Dessiner un élément HTML en tant que texture WebGPU

Dans WebGPU, les éléments HTML sont importés dans le GPU en tant que textures 2D à l'aide de drawElementImageToTexture().

Préallouer l'espace de stockage des textures

WebGPU nécessite une allocation de mémoire explicite. Avant de copier des pixels DOM dans une texture, vous devez réserver de la VRAM GPU à l'aide de la méthode 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,
});

Dessiner l'image de l'élément HTML

Dans WebGPU, la méthode drawElementImageToTexture() dessine l'instantané de l'élément HTML. La méthode utilise deux objets de dictionnaire : source et destination.

source définit l'élément DOM source à pixéliser et les coordonnées de découpage du sous-rectangle facultatives qui vous permettent de ne copier qu'une partie de l'élément dans votre texture plutôt que l'intégralité du nœud DOM :

Propriété Type Description
Minimum requis : source Element ou ElementImage Élément DOM cible (ou instance ElementImage précapturée) à dessiner dans la texture.
sourceX facultatif (les quatre ou aucun) float Décalage horizontal de la coordonnée X (en pixels CSS) définissant l'angle supérieur gauche de la tranche du sous-rectangle source.
sourceY facultatif (les quatre ou aucun) float Décalage vertical de la coordonnée Y (en pixels CSS) définissant l'angle supérieur gauche de la tranche du sous-rectangle source.
sourceWidth facultatif (les quatre ou aucun) float Largeur (en pixels CSS) du sous-rectangle source à recadrer et à pixéliser.
sourceHeight facultatif (les quatre ou aucun) float Hauteur (en pixels CSS) du sous-rectangle source à recadrer et à pixéliser.

destination définit la texture GPU cible qui reçoit l'instantané DOM affiché, ainsi que les décalages de destination, les niveaux de mip et les dimensions de copie facultatifs pour placer les pixels dans une région spécifique de la texture :

Propriété Type Description
Minimum requis : texture GPUTexture Texture WebGPU de destination préallouée qui recevra l'instantané de pixels DOM affiché.
size facultative(s) GPUExtent3D Largeur et hauteur (en pixels de texture physique, par exemple { width, height }) pour mettre à l'échelle et pixéliser l'élément dans la texture de destination.
origin facultative(s) GPUOrigin3D Décalage de destination 3D (x, y, z) dans la texture où les pixels pixélisés seront écrits. La valeur par défaut est [0, 0, 0].
mipLevel facultative(s) GPUIntegerCoordinate Niveau de mipmap spécifique de la texture de destination dans laquelle écrire. La valeur par défaut est 0.
aspect facultative(s) GPUTextureAspect Spécifie l'aspect de la texture à copier ("all", "stencil-only" ou "depth-only"). La valeur par défaut est "all".
colorSpace facultative(s) PredefinedColorSpace Espace colorimétrique de destination (par exemple, "srgb", "display-p3") pour la conversion des couleurs lors de l'importation. La valeur par défaut est "srgb".
premultipliedAlpha facultative(s) booléen Indique si l'alpha doit être prémultiplié dans les canaux de couleur lors de l'importation. La valeur par défaut est false.

Importez l'élément dans votre texture dans l'événement paint en appelant la méthode 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);
});

Synchroniser la géométrie

Pour aligner les tests de positionnement DOM et les anneaux de sélection d'accessibilité avec la sortie du canevas affiché, créez une transformation DOMMatrix et appliquez-la avec la méthode 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 });

Étapes suivantes