HTML in WebGPU-Canvas

Das Rendern von HTML in einem WebGPU-Canvas bietet alle Vorteile von HTML in einem WebGL-Canvas, z. B. das Anwenden von Shader-Effekten auf das DOM oder das Einbinden von interaktivem HTML in 3D-Szenen. WebGPU bietet jedoch erhebliche Vorteile gegenüber WebGL.

In diesem Leitfaden wird gezeigt, wie HTML in einem WebGPU-Canvas gerendert wird. Weitere Informationen zu HTML-in-Canvas in der API-Übersicht

Zum Rendern von HTML in einem WebGPU-Canvas sind die folgenden Schritte erforderlich:

  1. Arbeitsfläche vorbereiten Legen Sie die richtigen Attribute deklarativ fest, damit der Browser weiß, dass er HTML in einem Canvas rendern soll. Diese Einrichtung ist für 2D-, WebGL- und WebGPU-Kontexte identisch. Informationen zum Einrichten des Canvas und zum Deklarieren von HTML-Elementen, die unabhängig voneinander gezeichnet werden sollen
  2. Wenden Sie den HTML-Code als Textur an:
    1. GPU-Texturspeicher vorab zuweisen.
    2. Laden Sie den DOM-Snapshot als Textur hoch.
  3. Synchronisieren Sie das DOM und die Pixel.

HTML-Element als WebGPU-Textur zeichnen

In WebGPU werden HTML-Elemente mit drawElementImageToTexture() als 2D-Texturen auf die GPU hochgeladen.

Texturspeicher vorab zuweisen

WebGPU erfordert eine explizite Arbeitsspeicherzuweisung. Bevor Sie DOM-Pixel in eine Textur kopieren, müssen Sie mit der Methode device.createTexture() GPU-VRAM reservieren:

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,
});

HTML-Elementbild zeichnen

In WebGPU wird mit der Methode drawElementImageToTexture() der Snapshot des HTML-Elements gezeichnet. Die Methode verwendet zwei Wörterbuchobjekte: source und destination.

source definiert das zu rasternde DOM-Quellelement und optionale Koordinaten für das Zuschneiden eines Teilrechtecks, mit denen Sie nur einen Teil des Elements in Ihre Textur kopieren können, anstatt den gesamten DOM-Knoten:

Attribut Typ Beschreibung
source erforderlich Element oder ElementImage Das Ziel-DOM-Element (oder die zuvor erfasste ElementImage-Instanz), das in die Textur gezeichnet werden soll.
sourceX optional (alle vier oder keine) float Der horizontale X-Koordinaten-Offset (in CSS-Pixeln), der die linke obere Ecke des Quell-Unterrechteck-Schnitts definiert.
sourceY optional (alle vier oder keine) float Der vertikale Y-Koordinaten-Offset (in CSS-Pixeln), der die linke obere Ecke des Quell-Unter-Rechteck-Schnitts definiert.
sourceWidth optional (alle vier oder keine) float Die Breite (in CSS-Pixeln) des Quell-Teilrechtecks, das zugeschnitten und gerastert werden soll.
sourceHeight optional (alle vier oder keine) float Die Höhe (in CSS-Pixeln) des Quell-Teilrechtecks, das zugeschnitten und gerastert werden soll.

destination definiert die Ziel-GPU-Textur, die den gerenderten DOM-Snapshot empfängt, sowie optionale Ziel-Offsets, Mip-Level und Kopierdimensionen, um die Pixel in einem bestimmten Bereich der Textur zu platzieren:

Attribut Typ Beschreibung
texture erforderlich GPUTexture Die vorab zugewiesene WebGPU-Zieltextur, die den gerenderten DOM-Pixel-Snapshot empfängt.
size optional GPUExtent3D Die Breite und Höhe (in physischen Texturpixeln, z. B. { width, height }), um das Element innerhalb der Zieltextur zu skalieren und zu rastern.
origin optional GPUOrigin3D Der 3D-Zielversatz (x, y, z) innerhalb der Textur, in die die gerasterten Pixel geschrieben werden. Die Standardeinstellung ist [0, 0, 0].
mipLevel optional GPUIntegerCoordinate Die spezifische Mipmap-Ebene der Zieltextur, in die geschrieben werden soll. Die Standardeinstellung ist 0.
aspect optional GPUTextureAspect Gibt an, in welchen Texturaspekt kopiert werden soll ("all", "stencil-only" oder "depth-only"). Der Standardwert ist "all".
colorSpace optional PredefinedColorSpace Der Zielfarbraum (z. B. "srgb", "display-p3") für die Farbkonvertierung beim Upload. Die Standardeinstellung ist "srgb".
premultipliedAlpha optional boolean Gibt an, ob Alpha beim Upload in die Farbkanäle vormultipliziert werden soll. Die Standardeinstellung ist false.

Laden Sie das Element im paint-Ereignis in Ihre Textur hoch, indem Sie die Methode drawElementImageToTexture() aufrufen:

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);
});

Geometrie synchronisieren

Wenn Sie die DOM-Trefferprüfung und die Fokusringe für Bedienungshilfen an der gerenderten Canvas-Ausgabe ausrichten möchten, erstellen Sie eine DOMMatrix-Transformation und wenden Sie sie mit der Methode updateElementGeometry() an:

// 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 });

Nächste Schritte