HTML ใน Canvas ของ WebGPU

การแสดงผล HTML ภายใน Canvas ของ WebGPU มอบประโยชน์ทั้งหมดที่ HTML ใน Canvas ของ WebGL มี เช่น การใช้เอฟเฟกต์ตัวปรับเฉดสีกับ DOM หรือการผสานรวม HTML แบบอินเทอร์แอกทีฟเข้ากับฉาก 3 มิติ อย่างไรก็ตาม WebGPU มีข้อดีที่สำคัญเมื่อเทียบกับ WebGL

คู่มือนี้แสดงวิธีแสดงผล HTML ใน Canvas ของ WebGPU ดูข้อมูลเพิ่มเติมเกี่ยวกับ HTML-in-Canvas ในภาพรวม API

การแสดงผล HTML ใน Canvas ของ WebGPU ต้องทำตามขั้นตอนต่อไปนี้

  1. เตรียมผืนผ้าใบ ตั้งค่าแอตทริบิวต์ที่ถูกต้องอย่างชัดเจนเพื่อให้เบราว์เซอร์ทราบว่าควรแสดงผล HTML เป็น Canvas การตั้งค่านี้เหมือนกันในบริบท 2D, WebGL และ WebGPU ดูวิธีตั้งค่า Canvas และวิธีประกาศองค์ประกอบ HTML ที่จะวาดแยกกัน
  2. ใช้ HTML เป็นพื้นผิวโดยทำดังนี้
    1. จัดสรรพื้นที่เก็บข้อมูลพื้นผิว GPU ล่วงหน้า
    2. อัปโหลดภาพรวม DOM เป็นพื้นผิว
  3. ซิงค์ DOM และพิกเซล

วาดองค์ประกอบ HTML เป็นพื้นผิว WebGPU

ใน WebGPU ระบบจะอัปโหลดองค์ประกอบ HTML ไปยัง GPU เป็นพื้นผิว 2 มิติโดยใช้ drawElementImageToTexture()

จัดสรรพื้นที่เก็บข้อมูลพื้นผิวล่วงหน้า

WebGPU ต้องมีการจัดสรรหน่วยความจำอย่างชัดเจน ก่อนคัดลอกพิกเซล DOM ลงในพื้นผิว คุณต้องจอง GPU VRAM โดยใช้เมธอด 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 เมธอดนี้ใช้ออบเจ็กต์พจนานุกรม 2 รายการ ได้แก่ source และ destination

source กำหนดองค์ประกอบ DOM ต้นทางที่จะแรสเตอร์และพิกัดการตัดสี่เหลี่ยมผืนผ้าย่อยที่ไม่บังคับ ซึ่งช่วยให้คุณคัดลอกเฉพาะบางส่วนขององค์ประกอบลงในพื้นผิวแทนที่จะเป็นโหนด DOM ทั้งหมดได้ ดังนี้

พร็อพเพอร์ตี้ ประเภท คำอธิบาย
ต้องมี source Element หรือ ElementImage องค์ประกอบ DOM เป้าหมาย (หรืออินสแตนซ์ ElementImage ที่บันทึกไว้ล่วงหน้า) เพื่อวาดลงในพื้นผิว
sourceX ไม่บังคับ (ทั้ง 4 รายการหรือไม่มีเลย) จำนวนลอยตัว ออฟเซ็ตพิกัด X แนวนอน (เป็นพิกเซล CSS) ที่กำหนดมุมซ้ายบนของส่วนสี่เหลี่ยมผืนผ้าย่อยต้นทาง
sourceY ไม่บังคับ (ทั้ง 4 รายการหรือไม่มีเลย) จำนวนลอยตัว ออฟเซ็ตพิกัด Y แนวตั้ง (เป็นพิกเซล CSS) ที่กำหนดมุมซ้ายบนของส่วนสี่เหลี่ยมผืนผ้าย่อยต้นทาง
sourceWidth ไม่บังคับ (ทั้ง 4 รายการหรือไม่มีเลย) จำนวนลอยตัว ความกว้าง (เป็นพิกเซล CSS) ของสี่เหลี่ยมผืนผ้าย่อยต้นทางที่จะครอบตัดและแรสเตอร์
sourceHeight ไม่บังคับ (ทั้ง 4 รายการหรือไม่มีเลย) จำนวนลอยตัว ความสูง (เป็นพิกเซล CSS) ของสี่เหลี่ยมผืนผ้าย่อยต้นทางที่จะครอบตัดและแรสเตอร์

destination กำหนดพื้นผิว GPU เป้าหมายที่ได้รับภาพรวม DOM ที่แสดงผล พร้อมด้วยออฟเซ็ตปลายทาง ระดับ MIP และมิติข้อมูลการคัดลอกที่ไม่บังคับ เพื่อวางพิกเซลลงในบริเวณที่เฉพาะเจาะจงของพื้นผิว ดังนี้

พร็อพเพอร์ตี้ ประเภท คำอธิบาย
ต้องมี texture GPUTexture พื้นผิว WebGPU ปลายทางที่จัดสรรไว้ล่วงหน้าซึ่งจะได้รับภาพรวมพิกเซล DOM ที่แสดงผล
ระบุหรือไม่ก็ได้ size รายการ GPUExtent3D ความกว้างและความสูง (เป็นพิกเซลพื้นผิวจริง เช่น { width, height }) เพื่อปรับขนาดและแรสเตอร์องค์ประกอบภายในพื้นผิวปลายทาง
ระบุหรือไม่ก็ได้ origin รายการ GPUOrigin3D ออฟเซ็ตปลายทาง 3 มิติ (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 โดยเรียกใช้เมธอด 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 });

ขั้นตอนถัดไป