จาก WebGL สู่ WebGPU

François Beaufort
François Beaufort

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

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

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

สถานะทั่วโลก

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

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

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

ไม่ซิงค์อีกต่อไป

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

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

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

ตัวปรับเงาการประมวลผล

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

ไม่เหมือนกับ Vertex และ Fragment Shape ตรงที่ไม่จำกัดเพียงการประมวลผลกราฟิก และใช้ได้กับงานที่หลากหลาย เช่น แมชชีนเลิร์นนิง การจำลองฟิสิกส์ และการประมวลผลทางวิทยาศาสตร์ ตัวปรับแต่งเงาการประมวลผลจะทำงานพร้อมกันโดยชุดข้อความนับร้อยหรือนับพันรายการ ซึ่งทำให้การประมวลผลชุดข้อมูลขนาดใหญ่มีประสิทธิภาพอย่างมาก ดูข้อมูลเกี่ยวกับการประมวลผล 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() จะแสดงผลอุปกรณ์ GPU ที่อาจไม่ตรงกับความสามารถของฮาร์ดแวร์ของอุปกรณ์จริง แต่จะแสดงผลตัวส่วนร่วมที่สมเหตุสมผลและต่ำสุดของ GPU ทั้งหมด การกำหนดให้นักพัฒนาซอฟต์แวร์ขอขีดจำกัดอุปกรณ์ทำให้ WebGPU ทำให้แอปพลิเคชันทำงานได้บนอุปกรณ์จำนวนมากที่สุดเท่าที่จะเป็นไปได้

การจัดการภาพพิมพ์แคนวาส

WebGL จะจัดการ Canvas โดยอัตโนมัติหลังจากที่คุณสร้างบริบท WebGL และมอบแอตทริบิวต์บริบท เช่น Alpha, Analias, colorSpace, depth, delayDrawingBuffer หรือลายฉลุ

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

ดูการสาธิตเกี่ยวกับ WebGPU สำหรับ Canvas หลายรายการ

โปรดทราบว่าขณะนี้เบราว์เซอร์มีการจำกัดจำนวน Canvas ของ WebGL ต่อหน้าเว็บ ในขณะที่เขียน Chrome และ Safari สามารถใช้ Canvas ของ WebGL พร้อมกันได้สูงสุด 16 รายการเท่านั้น โดย Firefox สามารถสร้างได้สูงสุด 200 รายการ ในทางกลับกัน ไม่มีการจำกัดจำนวน Canvas ของ 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 จะสร้างระดับ mip อื่นๆ ทั้งหมดให้คุณ

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

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

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

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