מ-WebGL אל WebGPU

François Beaufort
François Beaufort

כמפתחי WebGL, אתם עשויים להיבהל ולהפסיק להשתמש ב-WebGPU, היורש של WebGL שמביא את הפיתוחים של ממשקי API גרפיים מודרניים לאינטרנט.

אפשר להיות בטוחים שאתם יודעים ש-WebGL ו-WebGPU חולקים הרבה מושגי ליבה. שני ממשקי ה-API מאפשרים להריץ תוכנות קטנות שנקראות תוכנות הצללה (shader) ב-GPU. WebGL תומך בתוכנות הצללה (shader) של קודקודים ומקטעים, בעוד ש-WebGPU תומך גם בתוכנות הצללה (shader) של מחשוב. טכנולוגיית WebGL משתמשת ב-OpenGL Shading Language (GLSL) ואילו ב-WebGPU נעשה שימוש בשפת הצללה של WebGPU (WGSL). שתי השפות שונות, אבל המושגים הבסיסיים זהים ברובם.

לכן, במאמר הזה נדגיש כמה הבדלים בין WebGL ל-WebGPU, כדי לעזור לכם להתחיל.

מצב גלובלי

ל-WebGL יש הרבה מצב גלובלי. חלק מההגדרות חלות על כל פעולות העיבוד, כמו למשל אילו טקסטורות ומאגרי נתונים זמניים קשורים. כדי להגדיר את המצב הגלובלי הזה באמצעות קריאה לפונקציות שונות של ה-API, הוא יישאר בתוקף עד שתשנו אותו. המצב הגלובלי ב-WebGL הוא מקור חשוב לשגיאות, כי קל לשכוח לשנות הגדרה גלובלית. בנוסף, מצב גלובלי מקשה על שיתוף הקוד, מכיוון שמפתחים צריכים להיזהר לא לשנות בטעות את המצב הגלובלי באופן שמשפיע על חלקים אחרים בקוד.

WebGPU הוא ממשק API ללא שמירת מצב, והוא לא שומר על מצב גלובלי. במקום זאת, הוא משתמש בתפיסה של צינור עיבוד נתונים כדי להקיף את כל מצב העיבוד שהיה גלובלי ב-WebGL. צינור עיבוד נתונים מכיל מידע כמו סוגי המיזוג, הטופולוגיה והמאפיינים שבהם צריך להשתמש. צינור עיבוד נתונים אינו ניתן לשינוי. כדי לשנות הגדרות מסוימות, צריך ליצור צינור עיבוד נתונים אחר. ב-WebGPU גם משתמשים במקודדי פקודות כדי לקבץ פקודות באצווה ולהפעיל אותן לפי הסדר שבו הן הוקלטו. האפשרות הזו שימושית במיפוי צללים, לדוגמה, כאשר, במעבר אחד על האובייקטים, האפליקציה יכולה להקליט מספר זרמי פקודות, אחד על מפת הצללים של כל אור.

לסיכום, מודל המצב הגלובלי של WebGL הפך את תהליך היצירה של ספריות ואפליקציות חזקות וניתנות לקומפוזביליות לקשות ושבריריות, ולכן WebGPU הפחית משמעותית את כמות המצב שהמפתחים היו צריכים לעקוב אחריו בזמן שליחת פקודות ל-GPU.

לא צריך יותר לסנכרן

במעבדי GPU לרוב לא יעיל לשלוח פקודות ולהמתין להן באופן סינכרוני, מכיוון שהדבר עלול לרוקן את צינור עיבוד הנתונים ולגרום לבועות. זה נכון במיוחד ב-WebGPU וב-WebGL, שמשתמשים בארכיטקטורה מרובת תהליכים כשמנהל ה-GPU פועל בתהליך נפרד מ-JavaScript.

לדוגמה, ב-WebGL, קריאה אל gl.getError() מחייבת IPC סינכרוני מתהליך ה-JavaScript לתהליך ה-GPU וחזרה ממנו. הדבר עלול לגרום לבועה בצד המעבד (CPU) בזמן ששני התהליכים מתקשרים.

כדי למנוע את הבועות האלה, WebGPU תוכנן להיות אסינכרוני לגמרי. מודל השגיאה וכל הפעולות האחרות מתרחשות באופן אסינכרוני. לדוגמה, בעת יצירת מרקם, נראה שהפעולה מצליחה מיד, גם אם המרקם הוא למעשה שגיאה. אפשר לגלות את השגיאה רק באופן אסינכרוני. בזכות העיצוב הזה, ההודעות בין תהליכים שונים נקיות מבועות, וביצועים אמינים של האפליקציות.

חישוב של תוכנות הצללה (shader)

תוכנות הצללה למחשוב הן תוכנות שרצות ב-GPU כדי לבצע חישובים לשימוש כללי. הם זמינים רק ב-WebGPU ולא ב-WebGL.

בשונה מכלי להצללה של קודקודים ומקטעים, הם לא מוגבלים לעיבוד גרפי ואפשר להשתמש בהם למגוון רחב של משימות, כמו למידת מכונה, הדמיית פיזיקה ומחשוב מדעי. תוכנות הצללה למחשוב מבוצעות במקביל על ידי מאות או אפילו אלפי שרשורים, ולכן הם יעילים מאוד בעיבוד מערכי נתונים גדולים. ניתן לקבל מידע נוסף על מחשוב GPU ופרטים נוספים במאמר המקיף הזה על WebGPU.

עיבוד הפריים של הווידאו

לעיבוד פריימים של וידאו באמצעות JavaScript ו-WebAssembly יש כמה חסרונות: עלות העתקת הנתונים מהזיכרון של ה-GPU לזיכרון של המעבד (CPU), ויכולת ההצגה האחידה מוגבלת שאפשר להשיג בעזרת עובדים ושרשורים של המעבד (CPU). ל-WebGPU אין את המגבלות האלה, ולכן הוא מתאים מאוד לעיבוד פריימים של וידאו בזכות השילוב ההדוק עם WebCodecs.

קטע הקוד הבא מראה איך לייבא 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 מבטיח שהאפליקציות יפעלו בכמה שיותר מכשירים.

טיפול בהדפסה על קנבס

WebGL מנהל באופן אוטומטי את אזור העריכה אחרי שיוצרים הקשר של WebGL ומספק מאפייני הקשר כמו alpha , antialias , colorSpace , עומק, trainDrawingBuffer או סטנסיל.

לעומת זאת, כדי להשתמש ב-WebGPU, תצטרכו לנהל את אזור העריכה בעצמכם. לדוגמה, כדי לבצע פעולת אנטי-לכך ב-WebGPU, עליכם ליצור מרקם מרובה דגימות ולעבד אותו. לאחר מכן, פותרים את המרקם מרובה הדגימות למרקם רגיל ומשרטטים אותו על אזור העריכה. הניהול הידני הזה מאפשר לכם ליצור פלט בכמה הדפסות על קנבס שרוצים מאובייקט GPUDevice יחיד. לעומת זאת, WebGL יכול ליצור הקשר אחד בלבד לכל אזור עריכה.

כדאי לעיין בהדגמה של WebGPU Multiple Canvases.

לידיעתך, לדפדפנים יש כרגע מגבלה על מספר הקנבס של WebGL בכל דף. בזמן הכתיבה, Chrome ו-Safari יכולים להשתמש בעד 16 קנבסים של WebGL בו-זמנית. Firefox יכול ליצור עד 200 מהם. מצד שני, אין הגבלה על מספר סביבת העריכה של WebGPU בכל דף.

צילום מסך עם מספר מקסימלי של הדפסות על קנבס ב-WebGL בדפדפני Safari, Chrome ו-Firefox
המספר המקסימלי של הדפסות על קנבס ב-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, אתם צריכים ליצור מפות מיפוי בעצמכם. אין פונקציה מובנית לביצוע הפעולה הזו. מידע נוסף על ההחלטה זמין בדיון על המפרט. תוכלו להשתמש בספריות שימושיות כמו webgpu-utils כדי ליצור מפות מיילים או ללמוד איך לעשות זאת בעצמכם.

אגמי אחסון ומרקמים של אחסון

מאגרי נתונים זמניים אחידים נתמכים גם ב-WebGL וגם ב-WebGPU, והם מאפשרים להעביר פרמטרים קבועים בגודל מוגבל לתוכנות הצללה. מאגרי נתונים זמניים באחסון, שנראים מאוד כמו מאגרי נתונים זמניים, נתמכים רק על ידי WebGPU והם חזקים וגמישים יותר ממאגרי אחסון אחידים.

  • הנתונים של מאגר הנתונים הזמני של האחסון שמועברים לתוכנות הצללה יכולים להיות גדולים בהרבה ממאגרי נתונים אחידים. לפי המפרט, קישורים של מאגרי נתונים זמניים אחידים יכולים להיות בגודל של עד 64KB (ראו maxUniformBufferBindingSize), אבל הגודל המקסימלי של קישור למאגר נתונים זמני של אחסון הוא לפחות 128MB ב-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.

אישורים

תודה לקורנטין ואלז, גרג טווארס, סטיבן ווייט, קן ראסל ורייצ'ל אנדרו שעיינו במאמר הזה.

אני גם ממליץ על WebGPUFundamentals.org כדי להבין לעומק את ההבדלים בין WebGPU לבין WebGL.