מ-WebGL אל WebGPU

פרנסואה בופורט
פרנסואה בופורט

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

כדאי להיות רגוע לדעת ש-WebGL ו-WebGPU חולקות מספר רב של מושגים מרכזיים. שני ממשקי ה-API מאפשרים להפעיל תוכנות קטנות שנקראות תוכנות הצללה ב-GPU. WebGL תומך בתוכנות הצללה של קודקוד וקטעים, וב-WebGPU יש גם תמיכה במצלילים. WebGL משתמש בשפת OpenGL Shading (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 מתוכנן להיות אסינכרוני לחלוטין. מודל השגיאה וכל שאר הפעולות מתרחשות באופן אסינכרוני. לדוגמה, בעת יצירת מרקם, נראה שהפעולה מצליחה באופן מיידי, גם אם המרקם הוא למעשה שגיאה. תוכלו לאתר את השגיאה באופן אסינכרוני בלבד. התכנון הזה לא יוצר בועות בתקשורת בין תהליכים, ומספק לאפליקציות ביצועים אמינים.

חישוב של תוכנות הצללה ל-Compute

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

בשונה מתוכנות הצללה של קודקודים ומקטעים, הם אינם מוגבלים לעיבוד גרפי, ואפשר להשתמש בהם למגוון רחב של משימות, כמו למידת מכונה, הדמיית פיזיקה ומחשוב מדעי. תוכנות הצללה של מחשוב מבוצעות במקביל באמצעות מאות או אפילו אלפי שרשורים, ולכן הם יעילים מאוד לעיבוד מערכי נתונים גדולים. מידע נוסף על GPU Compute ופרטים נוספים במאמר המפורט הזה על 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 מבטיח שאפליקציות יפעלו בכמה שיותר מכשירים.

טיפול בבד ציור

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

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

כדאי לנסות את ההדגמה של תבניות הדפסה מרובות ב-WebGPU.

לידיעתך, לדפדפנים יש כרגע מגבלה על מספר ההדפסות על קנבס של 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, הכול מקושר לחלוטין באמצעות היסט בייטים או אינדקס (שנקראים גם location). באחריותך לוודא שהמיקומים של הקוד ב-WGSL ו-JavaScript מסונכרנים.

יצירת Mipmap

ב-WebGL, אפשר ליצור Mip ברמה 0 של המרקם ואז להפעיל את gl.generateMipmap(). טכנולוגיית WebGL תיצור בשבילכם את כל רמות Mip האחרות.

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

מאגרי אחסון ומרקמי אחסון

מאגרי אחסון אחידים נתמכים גם על ידי 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.