מבני נתונים עיקריים והתפקיד שלהם ב-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

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

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

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

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

<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, שמכילה שני iframes.

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

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

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

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

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

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

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

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

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

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

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

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

רשימת ה-Waterfall של עדכון הנכס החזותי

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

תרשים של התהליך, שהוסבר בטקסט הקודם.

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

עץ המקטעים שאינו ניתן לשינוי

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

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

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

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

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

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

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

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

פריטי שבר בתוך השורה

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

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

הרשימה הרגילה נוצרת לכל הקשר של עיצוב מוטבע לפי הסדר של חיפוש עומק של עץ המשנה של הפריסה המוטבעת. כל רשומה ברשימה היא צמד של (אובייקט, מספר הצאצאים). לדוגמה, שקול את ה-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)
  • (תיבה <span>, 1)
  • (טקסט "שלום", 0)
  • (תיבת שורה, 3)
  • (תיבה <b>, 1)
  • (טקסט "שם", 0)
  • (טקסט '.', 0)

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

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

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

עצי נכס

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

התוצאות האלה מופיעות בעיקר בארבעה טעמים:

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

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

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

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

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

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

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

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

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

דוגמה

(מקור)

<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;שלום עולם&#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 שלילי לפני (שלב 3) ה-div הירוק (שלב 4.1). הפריטים בתצוגה תואמים, פחות או יותר, לשלבים האטומיים של מפרט סדר הצבעים ב-CSS. רכיב DOM יחיד עשוי לכלול מספר פריטי תצוגה, למשל, כיצד #green מכיל פריט תצוגה לרקע ופריט תצוגה אחר עבור הטקסט המוטבע. רמת הפירוט הזו חשובה לייצוג המורכבות המלאה של מפרט ההזמנה של צבע ה-CSS, למשל שיבוץ שנוצר על ידי שוליים שליליים:

מלבן ירוק עם תיבה אפורה שבחלקה שכבת-על, והמילים &#39;שלום עולם&#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 והטקסט "אני נופל".

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

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

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

רצוי שהדוגמה הקודמת תיצור שתי שכבות מורכבות:

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

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

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

  • רקע המסמך: גלילה במסמך, קטע טקסט, בסיס, גלילה במסמך.
  • פינה אופקית, אנכית ופינה גלילה עבור div (שלוש מקטעי צבע נפרדים): גלילת מסמכים, קליפ מסמך, טשטוש #one, גלילת מסמך.
  • iframe #one: סיבוב #one, חיתוך בגלילה לאפשרויות נוספות, טשטוש #one, גלילה ב-div.
  • iframe #two: קנה מידה של #two, קליפ מסמך, בסיס, גלילה במסמך.

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

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

כרטיס מידע

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

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

ארבעה משבצות.

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

ארבעה משטחים

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

אריחי מרקם של GPU.

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

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

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

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

אישורי עיבוד ביניים

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

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

צבירה

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

דוגמה

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

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

סיכום

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

איורים של אונה קראבטס.