จาก WebGL สู่ WebGPU

ฟร็องซัว โบฟอร์
François Beaufort

ในฐานะนักพัฒนาซอฟต์แวร์ WebGL คุณอาจรู้สึกหวาดกลัวและตื่นเต้นที่จะได้เริ่มใช้ WebGPU ซึ่งเป็นสิ่งที่สืบทอดมาจาก WebGL ที่นำความก้าวหน้าของ API กราฟิกสมัยใหม่มาสู่เว็บ

มั่นใจได้เลยว่า WebGL และ WebGPU มีแนวคิดหลักหลายอย่าง API ทั้งสองช่วยให้คุณเรียกใช้โปรแกรมขนาดเล็กที่เรียกว่าตัวให้เฉดสีบน GPU ได้ WebGL รองรับเวอร์เท็กซ์และตัวปรับแสงเงาบางส่วน ในขณะที่ WebGPU ยังรองรับตัวปรับแสงเงาสำหรับการประมวลผลด้วย WebGL ใช้ OpenGL Shading Language (GLSL) ขณะที่ WebGPU ใช้ WebGPU Shading Language (WGSL) แม้ว่าทั้ง 2 ภาษาจะแตกต่างกัน แต่แนวคิดพื้นฐานส่วนใหญ่นั้นเหมือนกัน

ด้วยเหตุนี้ บทความนี้จะไฮไลต์ความแตกต่างบางประการระหว่าง WebGL และ WebGPU เพื่อช่วยคุณในการเริ่มต้น

รัฐทั่วโลก

WebGL มีสถานะระดับโลกมากมาย การตั้งค่าบางอย่างจะมีผลกับการดำเนินการแสดงผลทั้งหมด เช่น พื้นผิวและบัฟเฟอร์ใดที่ผูกพัน คุณตั้งค่าสถานะส่วนกลางนี้ได้ด้วยการเรียกใช้ฟังก์ชันต่างๆ ของ API ซึ่งจะมีผลจนกว่าคุณจะเปลี่ยนแปลง สถานะทั่วไปใน WebGL เป็นสาเหตุหลักที่ทำให้เกิดข้อผิดพลาดเนื่องจากอาจลืมที่จะเปลี่ยนการตั้งค่าส่วนกลางได้ง่ายๆ นอกจากนี้ สถานะที่เป็นสากลยังทำให้การแชร์โค้ดเป็นไปได้ยาก เนื่องจากนักพัฒนาซอฟต์แวร์ต้องระมัดระวังว่าไม่ได้เผลอเปลี่ยนแปลงสถานะทั่วโลกในลักษณะที่ส่งผลต่อส่วนอื่นๆ ของโค้ด

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

สรุปคือ เนื่องจากโมเดลสถานะทั่วโลกของ WebGL ทำให้การสร้างไลบรารีและแอปพลิเคชันต่างๆ ที่แข็งแกร่งและประกอบได้กลายเป็นสิ่งที่ยากและเปราะบาง WebGPU จึงลดปริมาณสถานะที่นักพัฒนาซอฟต์แวร์จำเป็นต้องติดตามในขณะที่ส่งคำสั่งไปยัง GPU ลงได้อย่างมาก

ไม่ซิงค์แล้ว

โดยทั่วไปแล้ว การส่งคำสั่งและรอพร้อมกันใน GPU มักจะไม่มีประสิทธิภาพ เนื่องจากอาจเป็นการล้างไปป์ไลน์และทำให้เกิดลูกโป่ง โดยเฉพาะอย่างยิ่งใน WebGPU และ WebGL ซึ่งใช้สถาปัตยกรรมแบบมัลติโปรเซสกับไดรเวอร์ GPU ที่ทำงานในกระบวนการที่แยกจาก JavaScript

ตัวอย่างเช่น ใน WebGL การเรียก gl.getError() จะต้องใช้ IPC แบบซิงโครนัสจากกระบวนการ JavaScript ไปยังโปรเซสของ GPU และการทำงานของ GPU ซึ่งอาจทำให้เกิดฟองอากาศทางฝั่ง CPU ขณะที่ทั้ง 2 กระบวนการสื่อสารกัน

เพื่อหลีกเลี่ยงฟองอากาศเหล่านี้ WebGPU จึงได้รับการออกแบบให้เป็นแบบอะซิงโครนัสโดยสมบูรณ์ โมเดลข้อผิดพลาดและการดำเนินการอื่นๆ ทั้งหมดเกิดขึ้นแบบไม่พร้อมกัน ตัวอย่างเช่น เมื่อคุณสร้างพื้นผิว การดำเนินการที่ปรากฏจะสำเร็จทันที แม้ว่าพื้นผิวนั้นจะเป็นข้อผิดพลาดก็ตาม คุณจะพบข้อผิดพลาดแบบไม่พร้อมกันเท่านั้น การออกแบบนี้ช่วยให้การสื่อสารแบบข้ามกระบวนการปราศจากบับเบิลและช่วยให้แอปพลิเคชันมีประสิทธิภาพที่เชื่อถือได้

ตัวปรับแสงเงาในการประมวลผล

ตัวปรับแสงเงาสำหรับการประมวลผลคือโปรแกรมที่ทำงานบน GPU เพื่อคำนวณวัตถุประสงค์ทั่วไป โดยมีให้ใช้งานใน WebGPU เท่านั้น และจะไม่มี WebGL

เราไม่เพียงจำกัดการประมวลผลกราฟิก และสามารถใช้กับงานต่างๆ มากมาย เช่น แมชชีนเลิร์นนิง การจำลองทางฟิสิกส์ และการประมวลผลทางวิทยาศาสตร์ ซึ่งแตกต่างจากเครื่องมือหมุนจุดยอดและเฉดสีบางส่วน ตัวปรับเฉดสีสำหรับการประมวลผลจะดำเนินการพร้อมกันโดยเทรดนับร้อยหรือนับพันรายการ ซึ่งทำให้ประมวลผลชุดข้อมูลขนาดใหญ่ได้อย่างมีประสิทธิภาพมาก ดูข้อมูลเกี่ยวกับการประมวลผล GPU และรายละเอียดเพิ่มเติมในบทความที่ครอบคลุมเกี่ยวกับ WebGPU นี้

การประมวลผลเฟรมวิดีโอ

การประมวลผลเฟรมวิดีโอโดยใช้ JavaScript และ WebAssembly มีข้อเสียบางประการ ได้แก่ ค่าใช้จ่ายในการคัดลอกข้อมูลจากหน่วยความจำ GPU ไปยังหน่วยความจำ CPU และความทำงานได้พร้อมกันแบบจํากัดที่ผู้ปฏิบัติงานและเทรด CPU ทำได้ WebGPU ไม่มีข้อจำกัด ทำให้เหมาะสำหรับการประมวลผลเฟรมวิดีโออย่างมากเพราะผสานรวมกับ WebCodecs API อย่างลงตัว

ข้อมูลโค้ดต่อไปนี้แสดงวิธีนำเข้า VideoFrame เป็นพื้นผิวภายนอกใน WebGPU และประมวลผล คุณทดลองใช้การสาธิตนี้ได้

// Init WebGPU device and pipeline...
// Configure canvas context...
// Feed camera stream to video...

(function render() {
  const videoFrame = new VideoFrame(video);
  applyFilter(videoFrame);
  requestAnimationFrame(render);
})();

function applyFilter(videoFrame) {
  const texture = device.importExternalTexture({ source: videoFrame });
  const bindgroup = device.createBindGroup({
    layout: pipeline.getBindGroupLayout(0),
    entries: [{ binding: 0, resource: texture }],
  });
  // Finally, submit commands to GPU
}

ความสามารถในการถ่ายโอนแอปพลิเคชันโดยค่าเริ่มต้น

WebGPU บังคับให้คุณขอ limits โดยค่าเริ่มต้น requestDevice() จะแสดงผล GPUDevice ที่อาจไม่ตรงกับความสามารถของฮาร์ดแวร์ของอุปกรณ์จริง แต่แสดงผลที่ตัวส่วนทั่วไปที่สมเหตุสมผลและต่ำที่สุดของ GPU ทั้งหมด การกำหนดให้นักพัฒนาซอฟต์แวร์ต้องขอขีดจำกัดอุปกรณ์ทำให้ WebGPU ช่วยให้แอปพลิเคชันต่างๆ ทำงานบนอุปกรณ์ได้มากที่สุดเท่าที่จะเป็นไปได้

การจัดการ Canvas

WebGL จัดการผืนผ้าใบโดยอัตโนมัติหลังจากที่คุณสร้างบริบท WebGL และส่งแอตทริบิวต์บริบท เช่น อัลฟ่า, Antialia, colorSpace, ความลึก,reservationDrawingBuffer หรือลายฉลุ

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

ดูการสาธิต WebGPU Multiple Canvases

โปรดทราบว่าปัจจุบันเบราว์เซอร์มีการจำกัดจำนวน Canvas ของ WebGL ต่อหน้าเว็บ ในขณะที่เขียน Chrome และ Safari สามารถใช้แคนวาส WebGL พร้อมกันได้สูงสุด 16 รายการ ส่วน Firefox สร้างแคนวาสได้ถึง 200 รายการ ในทางกลับกัน ไม่มีการจำกัดจำนวนแคนวาส WebGPU ต่อหน้า

ภาพหน้าจอแสดงจำนวนสูงสุดของ Canvas ของ WebGL ในเบราว์เซอร์ Safari, Chrome และ Firefox
จำนวนสูงสุดของ Canvas ของ WebGL ใน Safari, Chrome และ Firefox (จากซ้ายไปขวา) - การสาธิต

ข้อความแสดงข้อผิดพลาดที่มีประโยชน์

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

นอกจากจะมีการเรียกใช้สแต็กแล้ว ข้อความแสดงข้อผิดพลาด WebGPU ยังอ่านเข้าใจง่ายและดำเนินการได้อีกด้วย โดยทั่วไปข้อความแสดงข้อผิดพลาดจะมีคําอธิบายข้อผิดพลาดและคําแนะนําเกี่ยวกับวิธีแก้ไขข้อผิดพลาด

นอกจากนี้ WebGPU ยังช่วยให้คุณระบุ label ที่กำหนดเองสำหรับออบเจ็กต์ WebGPU แต่ละรายการได้ด้วย จากนั้นเบราว์เซอร์จะใช้ป้ายกำกับนี้ในข้อความ GPUError, คำเตือนของคอนโซล และเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์เบราว์เซอร์

จากชื่อสู่ดัชนี

ใน WebGL หลายอย่างมีการเชื่อมต่อกันด้วยชื่อ เช่น คุณประกาศตัวแปรแบบเดียวกันที่ชื่อ myUniform ใน GLSL และรับตำแหน่งโดยใช้ gl.getUniformLocation(program, 'myUniform') ได้ วิธีนี้มีประโยชน์เมื่อคุณได้รับข้อผิดพลาด หากพิมพ์ชื่อตัวแปรแบบเดียวกันผิด

ในทางกลับกัน ใน WebGPU ทุกสิ่งจะเชื่อมต่อกันด้วยไบต์ออฟเซ็ตหรือดัชนี (มักเรียกว่าตำแหน่ง) คุณมีหน้าที่ต้องซิงค์ตำแหน่งของโค้ดใน WGSL และ JavaScript ให้ตรงกัน

การสร้าง Mipmap

ใน WebGL คุณสามารถสร้างพื้นผิวในระดับ 0 ไมล์แล้วเรียก gl.generateMipmap() จากนั้น WebGL จะสร้างระดับอื่นๆ ทั้งหมดให้คุณ

ใน WebGPU คุณต้องสร้าง mipmaps ด้วยตนเอง โดยจะไม่มีฟังก์ชันในตัว ดูการสนทนาเกี่ยวกับข้อกำหนดเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับคำตัดสิน คุณสามารถใช้ไลบรารีที่มีประโยชน์ เช่น webgpu-utils เพื่อสร้าง mipmaps หรือเรียนรู้วิธีทำด้วยตนเอง

บัฟเฟอร์และพื้นที่เก็บข้อมูล

บัฟเฟอร์ที่สม่ำเสมอได้รับการสนับสนุนโดยทั้ง WebGL และ WebGPU และช่วยให้คุณสามารถส่งผ่านพารามิเตอร์คงที่ที่มีขนาดจำกัดไปยังตัวปรับแสง บัฟเฟอร์พื้นที่เก็บข้อมูลซึ่งมีลักษณะคล้ายกับบัฟเฟอร์แบบเดียวกัน มีเพียง WebGPU เท่านั้นที่รองรับ โดยมีประสิทธิภาพและยืดหยุ่นมากกว่าบัฟเฟอร์แบบเดียวกัน

  • ข้อมูลบัฟเฟอร์พื้นที่เก็บข้อมูลที่ส่งไปยังตัวให้เฉดสีอาจมากกว่าบัฟเฟอร์แบบเดียวกันอย่างมาก แม้ว่าข้อกำหนดจะระบุว่าการเชื่อมโยงบัฟเฟอร์แบบสม่ำเสมอจะมีขนาดได้ไม่เกิน 64 KB (ดู maxUniformBufferBindingSize) แต่ขนาดสูงสุดของการเชื่อมโยงบัฟเฟอร์พื้นที่เก็บข้อมูลคืออย่างน้อย 128 MB ใน WebGPU (ดู maxStorageBufferBindingSize)

  • บัฟเฟอร์พื้นที่เก็บข้อมูลเขียนได้และรองรับการทำงานของอะตอมบางส่วน ในขณะที่บัฟเฟอร์แบบเดียวกันจะเป็นแบบอ่านอย่างเดียว วิธีนี้จะช่วยให้สามารถนำอัลกอริทึมใหม่ๆ ไปใช้ได้

  • การเชื่อมโยงบัฟเฟอร์พื้นที่เก็บข้อมูลรองรับอาร์เรย์ขนาดรันไทม์เพื่อให้อัลกอริทึมมีความยืดหยุ่นมากขึ้น ในขณะที่ต้องระบุขนาดอาร์เรย์บัฟเฟอร์แบบเดียวกันในตัวสร้างเงา

พื้นผิวของพื้นที่เก็บข้อมูลจะมีการสนับสนุนเฉพาะใน WebGPU เท่านั้น และใช้เพื่อสร้างพื้นผิวของบัฟเฟอร์พื้นที่เก็บข้อมูลเพื่อให้บัฟเฟอร์แบบเดียวกัน พื้นผิวเหล่านี้ยืดหยุ่นกว่าพื้นผิวปกติ รองรับการเขียนแบบเข้าถึงแบบสุ่ม (และการอ่านด้วยในอนาคต)

การเปลี่ยนแปลงบัฟเฟอร์และพื้นผิว

ใน WebGL คุณสามารถสร้างบัฟเฟอร์หรือพื้นผิว แล้วเปลี่ยนขนาดได้ตลอดเวลาโดยใช้ gl.bufferData() และ gl.texImage2D() ตามลำดับ

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

ความแตกต่างของรูปแบบพื้นที่ทำงาน

ใน WebGL ช่วงคลิปของ Z จะอยู่ระหว่าง -1 ถึง 1 ใน WebGPU ช่วงระยะห่างของคลิป Z อยู่ระหว่าง 0 ถึง 1 ซึ่งหมายความว่าวัตถุที่มีค่า z เป็น 0 จะอยู่ใกล้กับกล้องมากที่สุด ส่วนวัตถุที่มีค่า z เป็น 1 จะอยู่ไกลที่สุด

ภาพประกอบแสดงช่วงพื้นที่คลิป Z ใน WebGL และ WebGPU
ช่วงระยะห่างของช่องว่างแบบ Z ใน WebGL และ WebGPU

WebGL ใช้รูปแบบ OpenGL ซึ่งแกน Y ขึ้นและแกน Z ไปทางมุมมอง WebGPU ใช้รูปแบบโลหะ ซึ่งแกน Y คว่ำลงและแกน Z อยู่นอกหน้าจอ โปรดทราบว่าทิศทางแกน Y จะอยู่ในพิกัดเฟรมบัฟเฟอร์, พิกัดวิวพอร์ต และพิกัดส่วนย่อย/พิกเซล ในพื้นที่คลิป ทิศทางแกน Y ยังคงตั้งขึ้นเหมือนใน WebGL

กิตติกรรมประกาศ

ขอขอบคุณ Corentin Wallez, Gregg Tavares, Stephen White, Ken Russell และ Rachel Andrew ที่อ่านบทความนี้

นอกจากนี้ เราขอแนะนำให้คุณไปที่ WebGPUFundamentals.org เพื่อดูความแตกต่างอย่างละเอียดระหว่าง WebGPU และ WebGL