רינדור HTML בתוך קנבס של WebGPU מציע את כל היתרונות של HTML בקנבס של WebGL, כמו החלת אפקטים של הצללה על ה-DOM או שילוב של HTML אינטראקטיבי בסצנות תלת-ממדיות. עם זאת, ל-WebGPU יש יתרונות משמעותיים בהשוואה ל-WebGL.
במדריך הזה מוסבר איך לעבד HTML ב-canvas של WebGPU. מידע נוסף על HTML-in-Canvas בסקירה הכללית על ה-API
כדי לעבד HTML ב-canvas של WebGPU, צריך לבצע את השלבים הבאים:
- מכינים את הקנבס. מגדירים באופן הצהרתי את המאפיינים הנכונים כדי שהדפדפן ידע שהוא צריך לעבד HTML לתוך קנבס. ההגדרה הזו זהה בהקשרים של 2D, WebGL ו-WebGPU. איך מגדירים את ה-canvas ואיך מגדירים רכיבי HTML שיאוירו באופן עצמאי.
- מחילים את ה-HTML כטקסטורה:
- הקצאה מראש של אחסון טקסטורות ב-GPU.
- מעלים את תמונת המצב של ה-DOM כמרקם.
- סנכרון ה-DOM והפיקסלים.
ציור של רכיב HTML כטקסטורה של WebGPU
ב-WebGPU, רכיבי HTML מועלים ל-GPU כטקסטורות דו-ממדיות באמצעות drawElementImageToTexture().
הקצאה מראש של אחסון טקסטורות
ב-WebGPU נדרשת הקצאת זיכרון מפורשת. לפני שמעתיקים פיקסלים של DOM לטקסטורה, צריך להקצות VRAM של GPU באמצעות השיטה 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 אופציונלי (כל ארבעת המאפיינים או אף אחד מהם) |
מספר ממשי (float) | ההיסט האופקי של קואורדינטת ה-X (בפיקסלים של CSS) שמגדיר את הפינה השמאלית העליונה של פרוסת המלבן המשני של המקור. |
sourceY אופציונלי (כל ארבעת המאפיינים או אף אחד מהם) |
מספר ממשי (float) | ההיסט האנכי של קואורדינטת ה-Y (בפיקסלים של CSS) שמגדיר את הפינה השמאלית העליונה של פרוסת מלבן המשנה של המקור. |
sourceWidth אופציונלי (כל ארבעת המאפיינים או אף אחד מהם) |
מספר ממשי (float) | הרוחב (בפיקסלים של CSS) של המלבן המשני במקור שצריך לחתוך ולבצע בו רסטריזציה. |
sourceHeight אופציונלי (כל הארבעה או אף אחד) |
מספר ממשי (float) | הגובה (בפיקסלים של CSS) של המלבן המשני במקור שצריך לחתוך ולבצע בו רסטריזציה. |
destination מגדיר את טקסטורת היעד של ה-GPU שמקבלת את תמונת המצב שעברה רינדור של ה-DOM, יחד עם היסטים אופציונליים של היעד, רמות mip וממדי העתקה כדי למקם את הפיקסלים באזור ספציפי של הטקסטורה:
| נכס | סוג | תיאור |
|---|---|---|
הדרישה היא texture |
GPUTexture |
הטקסטורה של WebGPU ביעד שהוקצה מראש, שתקבל את תמונת המצב של הפיקסלים של ה-DOM שעברו רינדור. |
מספר השאלות האופציונליות: size |
GPUExtent3D |
הרוחב והגובה (בפיקסלים של טקסטורה פיזית, לדוגמה, { width, height }) כדי לשנות את קנה המידה ולבצע רסטריזציה של הרכיב בתוך טקסטורת היעד. |
מספר השאלות האופציונליות: origin |
GPUOrigin3D |
ההיסט של יעד התלת-ממד (x, y, z) בתוך הטקסטורה שבה ייכתבו הפיקסלים שעברו רסטריזציה. ברירת המחדל היא [0, 0, 0]. |
מספר השאלות האופציונליות: mipLevel |
GPUIntegerCoordinate |
רמת ה-mipmap הספציפית של טקסטורת היעד שאליה רוצים לכתוב. ברירת המחדל היא 0. |
מספר השאלות האופציונליות: aspect |
GPUTextureAspect |
מציין את היבט המרקם שאליו צריך להעתיק ("all", "stencil-only" או "depth-only"). ברירת המחדל היא "all". |
מספר השאלות האופציונליות: colorSpace |
PredefinedColorSpace |
מרחב הצבעים של היעד (לדוגמה, "srgb", "display-p3") להמרת צבעים בזמן ההעלאה. ברירת המחדל היא "srgb". |
מספר השאלות האופציונליות: premultipliedAlpha |
בוליאני | ההגדרה הזו קובעת אם להכפיל מראש את האלפא בערוצי הצבע במהלך ההעלאה. ברירת המחדל היא false. |
מעלים את הרכיב לטקסטורה בתוך האירוע paint באמצעות קריאה ל-method 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 });