מבני נתונים מרכזיים ב-RenderingNG

Chris Harrelson
Chris Harrelson
Daniel Cheng
Daniel Cheng
Philip Rogers
Philip Rogers
Koji Ishi
Koji Ishi
Ian Kilpatrick
Ian Kilpatrick
Kyle Charbonneau
Kyle Charbonneau

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

מבני הנתונים האלה הם:

  • עצי מסגרות מורכבים מצמתים מקומיים ומרוחקים שמייצגים את מסמכי האינטרנט שנמצאים בתהליך עיבוד מסוים ובמעבד עיבוד מסוים של Blink.
  • עץ הפאזל הבלתי ניתן לשינוי מייצג את הפלט (והקלט) של אלגוריתם אילוצי הפריסה.
  • עצים של נכסים מייצגים את היררכיות הטרנספורמציה, הקליפ, האפקט והגלילה של מסמך אינטרנט. הם משמשים לאורך צינור עיבוד הנתונים.
  • רשימות תצוגה וקטעי צביעה הם הקלט של אלגוריתמי הרסטור והשכבות.
  • פריימים של מערכי שכבות מכילים בתוכם משטחים, משטחי רינדור ומשבצות של טקסטורות GPU שמשמשות לציור באמצעות ה-GPU.

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

<!-- Example code -->
<html>
  <div style="overflow: hidden; width: 100px; height: 100px;">
    <iframe style="filter: blur(3px);
      transform: rotateZ(1deg);
      width: 100px; height: 300px"
      id="one" src="foo.com/etc"></iframe>
  </div>
  <iframe style="top:200px;
    transform: scale(1.1) translateX(200px)"
    id="two" src="bar.com"></iframe>
</html>

עצי מסגרות

לפעמים, Chrome עשוי לבחור להציג מסגרת ממקורות שונים בתהליך עיבוד שונה מזה של מסגרת ההורה שלה.

בקוד לדוגמה יש שלושה פריימים בסך הכול:

מסגרת הורה foo.com, שמכילה שתי מסגרות iframe.

כשמשתמשים בבידוד אתרים, מערכת Chromium משתמשת בשני תהליכי רינדור כדי להציג את דף האינטרנט הזה. לכל תהליך רינדור יש ייצוג משלו של עץ המסגרות של דף האינטרנט הזה:

שני עצי מסגרות שמייצגים את שני תהליכי הרינדור.

מסגרת שעברה עיבוד בתהליך אחר מיוצגת כמסגרת מרוחקת. מסגרת מרוחקת מכילה את המידע המינימלי הנדרש כדי לשמש כ placeholder (מקום להטמעת רכיב) ברינדור, למשל המימדים שלה. אחרת, המסגרת המרוחקת לא מכילה מידע נדרש לעיבוד התוכן בפועל שלה.

לעומת זאת, מסגרת מקומית מייצגת מסגרת שעוברת דרך צינור עיבוד הנתונים הרגיל לעיבוד תמונה. המסגרת המקומית מכילה את כל המידע הדרוש כדי להפוך את הנתונים של המסגרת הזו (כמו עץ ה-DOM ונתוני הסגנון) למשהו שאפשר ליצור ולציג.

צינור עיבוד הנתונים לעיבוד פועל ברמת הפירוט של קטע מקומי של עץ המסגרות. דוגמה מורכבת יותר שבה foo.com הוא המסגרת הראשית:

<iframe src="bar.com"></iframe>

וגם את תת-המסגרת bar.com הבאה:

<iframe src="foo.com/etc"></iframe>

עדיין יש רק שני מעבדי גרפיקה, אבל עכשיו יש שלושה קטעי עץ מסגרות מקומיים, שניים בתהליך העיבוד של foo.com ואחד בתהליך העיבוד של bar.com:

ייצוג של שני הרנדרים ושלושה קטעים של עץ המסגרות.

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

המסגרת הראשית foo.com והמסגרת המשנית foo.com/other-page הם חלק מאותו עץ מסגרות, והם עוברים עיבוד באותו תהליך. עם זאת, לשני המסגרות עדיין יש מחזורי חיים נפרדים של מסמכים, כי הם חלק מקטעים שונים של עץ המסגרות המקומי. לכן, אי אפשר ליצור מסגרת אחת של רכיב עיבוד התמונה בשני העדכונים. לתהליך הרינדור אין מספיק מידע כדי ליצור קומפוזיציה של מסגרת המאגר שנוצרה עבור foo.com/other-page ישירות במסגרת המאגר של המסגרת הראשית foo.com. לדוגמה, מסגרת ההורה bar.com מחוץ לתהליך עשויה להשפיע על התצוגה של ה-iframe‏ foo.com/other-url, על ידי טרנספורמציה של ה-iframe באמצעות CSS או על ידי חסימת חלקים מה-iframe באמצעות רכיבים אחרים ב-DOM שלו.

תרשים של שלבי עדכון הנכס החזותי

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

לדוגמה, כשגודל אזור התצוגה משתנה:

תרשים של התהליך שמתואר בטקסט שלמעלה.

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

עץ הפאזלים הבלתי משתנה

עץ הפסקאות הקבוע הוא הפלט של שלב הפריסה בצינור עיבוד הנתונים של הרינדור. הוא מייצג את המיקום והגודל של כל הרכיבים בדף (ללא טרנספורמציות).

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

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

אחרי הפריסה, כל קטע הופך לבלתי ניתן לשינוי ולא ניתן לשנות אותו שוב. חשוב לציין שאנחנו גם מטילים כמה הגבלות נוספות. אנחנו לא:

  • מתן הרשאה לכל הפניות 'מעלה' בעץ. (לצאצא לא יכול להיות מפנה להורה שלו).
  • 'להעביר' נתונים במורד העץ (צאצא קורא מידע רק מהצאצאים שלו, ולא מההורה).

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

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

בעתיד, העיצוב הבלתי ניתן לשינוי הזה יכול לאפשר לנו לעשות דברים מעניינים, כמו להעביר את עץ הקטעים הבלתי ניתן לשינוי מעבר לגבולות של חוטים אם יש צורך (כדי לבצע שלבים הבאים בשרשור אחר), ליצור כמה עצים לצורך אנימציה חלקה של פריסה או לבצע פריסות ספקולטיביות במקביל. הוא גם מאפשר לנו להשתמש בתצוגה עם כמה שרשורים (multi-threading).

פריטים של קטעי טקסט מוטמעים

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

הרשימה הרגילה נוצרת לכל הקשר של עיצוב בתוך שורה לפי סדר החיפוש לעומק של עץ המשנה של הפריסה בתוך השורה. כל רשומה ברשימה היא קבוצה של שני ערכים (object, מספר הצאצאים). לדוגמה, ה-DOM הזה:

<div style="width: 0;">
  <span style="color: blue; position: relative;">Hi</span> <b>there</b>.
</div>

הערך של המאפיין width מוגדר כ-0 כדי שהשורה תתבצע בין 'היי' לבין 'שם'.

כשהקשר של העיצוב בתוך השורה למקרה הזה מיוצג כעץ, הוא נראה כך:

{
  "Line box": {
    "Box <span>": {
      "Text": "Hi"
    }
  },
  "Line box": {
    "Box <b>": {
      "Text": "There"
    }
  },
  {
    "Text": "."
  }
}

הרשימה הרגילה נראית כך:

  • (קופסת שורה, 2)
  • (Box <span>, 1)
  • (טקסט 'היי', 0)
  • (קופסת שורה, 3)
  • (Box <b>, 1)
  • (Text "there", 0)
  • (Text ".", 0)

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

לסמן יש ממשקי API, כמו MoveToNext,‏ MoveToNextLine ו-CursorForChildren. ייצוג הסמן הזה שימושי מאוד לתוכן טקסט, מכמה סיבות:

  • איטרציה בסדר החיפוש לעומק היא מהירה מאוד. האפשרות הזו משמשת לעיתים קרובות כי היא דומה לתנועות של הסמן. מכיוון שזו רשימה רגילה, חיפוש עומק-ראשית הוא רק הגדלת ההיסט של המערך, שמספק איטרציות מהירות ומיקום בזיכרון.
  • הוא מספק חיפוש 'רוחב-ראשית', שנחוץ, למשל, כשמציירים את הרקע של קווים ותיבות בתוך שורה.
  • כשמביאים בחשבון את מספר הצאצאים, אפשר לעבור במהירות לאח הבא (פשוט מוסיפים את המספר הזה להיסט של המערך).

עצי נכסים

ה-DOM הוא עץ של רכיבים (וכן צמתים של טקסט), ו-CSS יכול להחיל סגנונות שונים על הרכיבים.

הוא מופיע בארבע דרכים:

  • פריסה: קלט לאלגוריתם האילוצים של הפריסה.
  • Paint: איך לצייר את הרכיב ולבצע רסטורציה שלו (אבל לא של הצאצאים שלו).
  • חזותיים: אפקטים של ציור/רסטרים שחלים על עץ המשנה של DOM, כמו טרנספורמציות, פילטרים וקיצוץ.
  • גלילה: חיתוך לפי ציר ועיגול פינות וגלילה של עץ המשנה הכלול.

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

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

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

מערכת RenderingNG משתמשת בעצי נכסים למטרות רבות, כולל:

  • הפרדת הרכבה (compositing) מצביעה (paint), והרכבה מהשרשור הראשי.
  • קביעת אסטרטגיית הרכבה או ציור אופטימלית.
  • מדידת הגיאומטריה של IntersectionObserver.
  • הימנעות מעבודה על אלמנטים מחוץ למסך ועל משבצות של טקסטורות של GPU.
  • ביטול יעיל ומדויק של נתוני צביעה ונתוני רסטר.
  • מדידת הזזת הפריסה והמהירות שבה נטען רכיב התוכן הכי גדול (LCP) במדדי הליבה לבדיקת חוויית המשתמש באתר.

לכל מסמך אינטרנט יש ארבעה עצי מאפיינים נפרדים: transform,‏ clip,‏ effect ו-scroll.(*) עץ הטרנספורמציות מייצג טרנספורמציות וגלילה ב-CSS. (טרנספורמציית גלילה מיוצגת כמטריצת טרנספורמציה דו-ממדית). עץ הקליפים מייצג קליפים של עודף נתונים. עץ האפקטים מייצג את כל שאר האפקטים החזותיים: שקיפיות, פילטרים, מסיכות, שיטות מיזוג וסוגים אחרים של קליפים, כמו clip-path. עץ הגלילה מייצג מידע על גלילה, כמו האופן שבו שרשרת גלילות יחד. הוא נדרש כדי לבצע גלילה בשרשור של המאגר. כל צומת בעץ הנכסים מייצג גלילה או אפקט חזותי שהוחלו על ידי רכיב DOM. אם יש לו כמה אפקטים, יכול להיות שיהיו יותר מעמודה אחת של עץ נכסים בכל עץ של אותו רכיב.

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

לכל רכיב DOM יש מצב של עץ נכסים, שהוא קבוצה של 4 ערכים (transform,‏ clip,‏ effect,‏ scroll) שמציינת את צמתים העץ של הקליפ, הטרנספורמציה והאפקט של האב הקרוב ביותר שפועלים על הרכיב הזה. זה מאוד נוח, כי בעזרת המידע הזה אנחנו יודעים בדיוק את רשימת הקטעים, הטרנספורמציות והאפקטים שחלים על הרכיב הזה, ובאיזה סדר. כך נדע איפה הוא נמצא במסך ואיך לצייר אותו.

דוגמה

(source)

<html>
  <div style="overflow: scroll; width: 100px; height: 100px;">
    <iframe style="filter: blur(3px);
      transform: rotateZ(1deg);
      width: 100px; height: 300px"
  id="one" srcdoc="iframe one"></iframe>
  </div>
  <iframe style="top:200px;
      transform: scale(1.1) translateX(200px)" id=two
      srcdoc="iframe two"></iframe>
</html>

בדוגמה הקודמת (ששונה במקצת מהדוגמה שבמבוא), אלה הרכיבים העיקריים של עצי הנכסים שנוצרו:

דוגמה לאלמנטים השונים בעץ הנכסים.

הצגת רשימות וצביעת קטעים

פריט תצוגה מכיל פקודות ציור ברמה נמוכה (כאן) שאפשר לבצע להם רסטורציה באמצעות Skia. פריטים בתצוגה הם בדרך כלל פשוטים, עם כמה פקודות ציור בלבד, כמו ציור של גבול או רקע. ההליכה בעץ הציור חוזרת על עצמה בעץ הפריסה ובקטעים המשויכים לפי סדר הציור ב-CSS כדי ליצור רשימה של פריטים להצגה.

לדוגמה:

תיבה כחולה עם המילים &#39;Hello world&#39; בתוך מלבן ירוק.

<div id="green" style="background:green; width:80px;">
    Hello world
</div>
<div id="blue" style="width:100px;
  height:100px; background:blue;
  position:absolute;
  top:0; left:0; z-index:-1;">
</div>

הקוד הזה ב-HTML וב-CSS ייצור את רשימת התצוגה הבאה, שבה כל תא הוא פריט תצוגה:

הרקע של התצוגה #blue ברקע #green ברקע #green טקסט בשורה
drawRect בגודל 800x600 ובצבע לבן. drawRect בגודל 100x100 במיקום 0,0 ובצבע כחול. drawRect בגודל 80x18, במיקום 8,8 ובצבע ירוק. drawTextBlob עם המיקום 8,8 והטקסט 'Hello world'.

רשימת הפריטים המוצגים מסודרת מהסוף להתחלה. בדוגמה שלמעלה, ה-div הירוק מופיע לפני ה-div הכחול בסדר ה-DOM, אבל סדר הציור של CSS דורש שה-div הכחול עם הערך השלילי של z-index יתבצע לפני (שלב 3) ה-div הירוק (שלב 4.1). פריטים בתצוגה תואמים באופן גס לשלבים אטומיים במפרט של סדר הצביעה ב-CSS. רכיב DOM אחד יכול להוביל לכמה פריטים בתצוגה, למשל, ל-#green יש פריט תצוגה לרקע ופריט תצוגה נוסף לטקסט בשורה. רמת הפירוט הזו חשובה כדי לייצג את המורכבות המלאה של מפרט סדר הצביעה ב-CSS, כמו החלפה בין שכבות שנוצרת על ידי שוליים שליליים:

מלבן ירוק עם תיבה אפורה שמכסה אותו חלקית והמילים &#39;Hello world&#39;.

<div id="green" style="background:green; width:80px;">
    Hello world
</div>
<div id="gray" style="width:35px; height:20px;
  background:gray;margin-top:-10px;"></div>

הפעולה הזו יוצרת את רשימת התצוגה הבאה, שבה כל תא הוא פריט תצוגה:

הרקע של התצוגה #green ברקע #gray ברקע #green טקסט בשורה
drawRect בגודל 800x600 ובצבע לבן. drawRect בגודל 80x18, במיקום 8,8 ובצבע ירוק. drawRect בגודל 35x20 במיקום 8,16 ובצבע אפור. drawTextBlob עם המיקום 8,8 והטקסט 'Hello world'.

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

המצב הנוכחי של עץ הנכסים נשמר במהלך ההליכה בעץ הציור, ורשימת הפריטים המוצגים מקובצת ל'קטעים' של פריטים מוצגים שיש להם את אותו מצב בעץ הנכסים. הדוגמה הבאה ממחישה זאת:

קופסה ורודה עם קופסה כתומה מוטה.

<div id="scroll" style="background:pink; width:100px;
   height:100px; overflow:scroll;
   position:absolute; top:0; left:0;">
    Hello world
    <div id="orange" style="width:75px; height:200px;
      background:orange; transform:rotateZ(25deg);">
        I'm falling
    </div>
</div>

הפעולה הזו יוצרת את רשימת התצוגה הבאה, שבה כל תא הוא פריט תצוגה:

הרקע של התצוגה #scroll ברקע #scroll טקסט בשורה #orange ברקע #orange טקסט בשורה
drawRect בגודל 800x600 ובצבע לבן. drawRect בגודל 100x100 במיקום 0,0 ובצבע ורוד. drawTextBlob עם המיקום 0,0 והטקסט 'Hello world'. drawRect בגודל 75x200 במיקום 0,0 ובצבע כתום. drawTextBlob עם המיקום 0,0 והטקסט 'I'm falling'.

עץ מאפייני הטרנספורמציה וקובצי ה-paint יהיו אז (בקיצור לצורך הנוחות):

תמונה של הטבלה הקודמת, שני התאים הראשונים בחלק 1, השלישי בחלק 2, ושני התאים האחרונים בחלק 3.

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

באופן אידיאלי, הדוגמה הקודמת אמורה ליצור שתי שכבות מורכבות:

  • שכבת קומפוזיציה בגודל 800x600 שמכילה את פקודות הציור:
    1. drawRect בגודל 800x600 ובצבע לבן
    2. drawRect בגודל 100x100 במיקום 0,0 ובצבע ורוד
  • שכבת קומפוזיציה בגודל 144x224 שמכילה את פקודות הציור:
    1. drawTextBlob עם המיקום 0,0 והטקסט 'Hello world'
    2. translate 0,18
    3. rotateZ(25deg)
    4. drawRect בגודל 75x200 במיקום 0,0 ובצבע כתום
    5. drawTextBlob עם המיקום 0,0 והטקסט 'I'm falling'

אם המשתמש גולל #scroll, השכבה המשולבת השנייה מועברת, אבל אין צורך בביצוע רסטורציה.

בדוגמה, מהקטע הקודם על עצי נכסים, יש שישה קטעי צבע. יחד עם מצבי עץ הנכסים שלהם (טרנספורמציה, קליפ, אפקט, גלילה), הם:

  • רקע המסמך: גלילה במסמך, קליפ של מסמך, root, גלילה במסמך.
  • פינה אופקית, פינה אנכית ופינה של גלילה ל-div (שלושה קטעי צביעה נפרדים): גלילה במסמך, חיתוך של מסמך, טשטוש #one, גלילה במסמך.
  • Iframe #one: #one rotate, overflow scroll clip, #one blur, div scroll.
  • Iframe #two: #two scale, document clip, root, document scroll.

פריימים של מעבד הקומפוזיציה: משטחים, משטחי רינדור ואריחי טקסטורה של GPU

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

כרטיס מידע

בתיאוריה, מעבד תהליך עיבוד או מעבד תהליך דפדפן יכולים לבצע רסטורציה של פיקסלים לתוך טקסטורה אחת בגודל מלא של חלון התצוגה של ה-renderer, ולשלוח את הטקסטורה הזו ל-Viz. כדי להציג אותה, מעבד התצוגה יצטרך רק להעתיק את הפיקסלים מהטקסטורה היחידה הזו למיקום המתאים במאגר המסגרות (למשל, המסך). עם זאת, אם המאגר ירצה לעדכן אפילו פיקסל אחד, הוא יצטרך לבצע רסטריזציה מחדש של אזור התצוגה המלא ולשלוח טקסטורה חדשה ל-Viz.

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

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

ריבועים ומשטחים

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

משבצות של מרקמים ב-GPU.

המשבצות הרסטרליות האלה עטופות בתהליך רינדור, שהוא רשימה של ריבועים. תהליך ה-render pass לא מכיל מידע על פיקסלים, אלא הוראות לגבי המיקום והאופן שבהם צריך לצייר כל ריבוע כדי ליצור את פלט הפיקסלים הרצוי. יש draw quad לכל משבצת של טקסטורה ב-GPU. כדי ליצור את הפלט הרצוי של הפיקסלים לצורך שלב הרינדור, למעבד התצוגה פשוט צריך לעבור על רשימת הריבועים, ולצייר כל אחד מהם עם האפקטים החזותיים שצוינו. אפשר לבצע שילוב של ריבועים מצוירים לצורך שלב עיבוד (pass) ברנדור ביעילות ב-GPU, כי האפקטים החזותיים המותרים נבחרים בקפידה כך שיתאימו למיפוי ישיר לתכונות של ה-GPU.

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

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

כשרכיב עיבוד תמונה שולח פריים של רכיב עיבוד תמונה, הוא מצורף למזהה שנקרא מזהה פלטפורמה, שמאפשר לפריימים אחרים של רכיב עיבוד תמונה להטמיע אותו לפי הפניה. מסגרת המאגר החדשה ביותר שנשלחת עם מזהה פלטפורמה מסוים מאוחסנת ב-Viz. מסגרת מאגר אחרת יכולה להפנות אליה מאוחר יותר באמצעות surface draw quad, ולכן Viz יודע מה לצייר. (לתשומת ליבכם: ריבועים של ציור משטחים מכילים רק מזהי משטחים, ולא טקסטורות).

שלבי רינדור ביניים

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

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

צבירה

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

דוגמה

אלה המסגרות של המאגר שמייצגות את הדוגמה מתחילת הפוסט.

  • foo.com/index.html surface: id=0
    • תהליך עיבוד גרפי 0: ציור לפלט.
      • ציור של ריבוע ב-render pass: ציור עם טשטוש של 3px וקיצוץ ל-render pass 0.
        • תהליך עיבוד גרפי 1:
          • ציור של ריבועים לתוכן המשבצות של iframe‏ #one, עם מיקומי x ו-y לכל אחד מהם.
      • ריבוע ציור של משטח: עם מזהה 2, שצויר באמצעות טרנספורמציה של שינוי קנה מידה והעברה.
  • ממשק המשתמש בדפדפן: ID=1
    • תהליך עיבוד גרפי 0: ציור לפלט.
      • ציור ריבועים לממשק המשתמש בדפדפן (גם בחלוקה לריבועים)
  • bar.com/index.html surface: ID=2
    • תהליך עיבוד גרפי 0: ציור לפלט.
      • ציור ריבועים לתוכן של iframe‏ #two, עם מיקומי x ו-y לכל אחד מהם.

איורים של Una Kravets.