HTML на холсте WebGPU

Отрисовка HTML в холсте WebGPU дает те же преимущества, что и в холсте WebGL, например позволяет применять эффекты шейдеров к DOM или интегрировать интерактивный HTML в 3D-сцены. Однако WebGPU имеет значительные преимущества по сравнению с WebGL.

В этом руководстве показано, как отрисовать HTML на холсте WebGPU. Подробнее об HTML-in-Canvas в обзоре API…

Для рендеринга HTML на холсте WebGPU необходимо выполнить следующие шаги:

  1. Подготовьте холст. Задайте нужные атрибуты, чтобы браузер знал, что HTML-код нужно отрисовать в элементе canvas. Эта настройка одинакова для контекстов 2D, WebGL и WebGPU. Узнайте, как настроить холст и как объявить элементы HTML, которые будут отрисовываться независимо.
  2. Примените HTML-код в качестве текстуры:
    1. Предварительно выделяйте хранилище текстур графического процессора.
    2. Загрузите снимок DOM в качестве текстуры.
  3. Синхронизируйте DOM и пиксели.

Отрисовка элемента HTML в качестве текстуры WebGPU

В WebGPU элементы HTML загружаются в графический процессор как 2D-текстуры с помощью drawElementImageToTexture().

Предварительно выделяйте хранилище для текстур

WebGPU требует явного выделения памяти. Прежде чем копировать пиксели DOM в текстуру, необходимо зарезервировать видеопамять графического процессора с помощью метода 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,
});

Нарисовать изображение элемента HTML

В WebGPU метод drawElementImageToTexture() рисует снимок элемента HTML. Метод принимает два объекта словаря: source и destination.

source определяет исходный элемент DOM для растеризации и необязательные координаты обрезки подпрямоугольника, которые позволяют скопировать в текстуру только часть элемента, а не весь узел DOM:

Свойство Тип Описание
source (обязательно) Element или ElementImage Целевой элемент DOM (или предварительно захваченный экземпляр ElementImage), который нужно отрисовать в текстуре.
sourceX необязательно (все четыре или ни одного) число с плавающей запятой Горизонтальное смещение по оси X (в пикселях CSS), определяющее верхний левый угол исходного фрагмента.
sourceY – необязательный (все четыре или ни одного) число с плавающей запятой Вертикальное смещение по оси Y (в пикселях CSS), определяющее верхний левый угол исходного фрагмента.
sourceWidth необязательно (все четыре или ни одного) число с плавающей запятой Ширина (в пикселях CSS) исходного прямоугольника, который нужно обрезать и растрировать.
sourceHeight необязательно (все четыре или ни одного) число с плавающей запятой Высота (в пикселях CSS) исходного прямоугольника, который нужно обрезать и растрировать.

destination определяет целевую текстуру графического процессора, которая получает отрисованный снимок DOM, а также необязательные смещения назначения, уровни MIP и размеры копирования для размещения пикселей в определенной области текстуры:

Свойство Тип Описание
texture (обязательно) GPUTexture Предварительно выделенная целевая текстура WebGPU, которая получит отрисованный пиксельный снимок DOM.
Необязательных вопросов: size GPUExtent3D Ширина и высота (в пикселях физической текстуры, например { width, height }) для масштабирования и растеризации элемента в целевой текстуре.
Необязательных вопросов: origin GPUOrigin3D Трехмерное смещение (x, y, z) в текстуре, куда будут записаны растрированные пиксели. Значение по умолчанию – [0, 0, 0].
Необязательных вопросов: mipLevel GPUIntegerCoordinate Определенный уровень MIP-текстурирования целевой текстуры, в которую нужно выполнить запись. Значение по умолчанию – 0.
Необязательных вопросов: aspect GPUTextureAspect Указывает, какой аспект текстуры копировать ("all", "stencil-only" или "depth-only"). По умолчанию используется "all".
Необязательных вопросов: colorSpace PredefinedColorSpace Целевое цветовое пространство (например, "srgb", "display-p3") для преобразования цвета при загрузке. Значение по умолчанию – "srgb".
Необязательных вопросов: premultipliedAlpha Логическое значение Указывает, нужно ли предварительно умножать альфа-канал на цветовые каналы при загрузке. Значение по умолчанию – false.

Загрузите элемент в текстуру в событии paint, вызвав метод 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);
});

Синхронизировать геометрию

Чтобы выровнять проверку нажатия DOM и кольца фокуса специальных возможностей с отрисованным выводом canvas, создайте преобразование DOMMatrix и примените его с помощью метода 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 });

Дальнейшие действия