נבחן את מבני הנתונים העיקריים, שהם קלט ופלט של צינור העיבוד ליצירת הגרפיקה.
מבני הנתונים האלה הם:
- עצי מסגרות מורכבים מצמתים מקומיים ומרוחקים שמייצגים את מסמכי האינטרנט שנמצאים בתהליך עיבוד מסוים ובמעבד עיבוד מסוים של 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 עשוי לבחור להציג מסגרת ממקורות שונים בתהליך עיבוד שונה מזה של מסגרת ההורה שלה.
בקוד לדוגמה יש שלושה פריימים בסך הכול:
כשמשתמשים בבידוד אתרים, מערכת Chromium משתמשת בשני תהליכי רינדור כדי להציג את דף האינטרנט הזה. לכל תהליך עיבוד יש ייצוג משלו של עץ המסגרות של אותו דף אינטרנט:
מסגרת שעברה עיבוד בתהליך אחר מיוצגת כמסגרת מרוחקת. מסגרת מרוחקת מכילה את המידע המינימלי הנדרש כדי לשמש כ placeholder (מקום להטמעת רכיב) ברינדור, למשל המימדים שלה. אחרת, המסגרת המרוחקת לא מכילה מידע נדרש לעיבוד התוכן בפועל שלה.
לעומת זאת, מסגרת מקומית מייצגת מסגרת שעוברת דרך צינור עיבוד הנתונים הרגיל לעיבוד (render). המסגרת המקומית מכילה את כל המידע הדרוש כדי להפוך את הנתונים של המסגרת הזו (כמו עץ ה-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 שלו.
תרשים של שלבי עדכון הנכס החזותי
מאפיינים חזותיים כמו גורם קנה המידה של המכשיר וגודל אזור התצוגה משפיעים על הפלט שעבר רינדור, וצריך לסנכרן אותם בין קטעי עץ הפריימים המקומיים. לשורש של כל קטע מקומי של עץ המסגרות משויך אובייקט ווידג'ט. עדכונים של מאפיינים חזותיים מועברים לווידג'ט של המסגרת הראשית, ואז מופצים לשאר הווידג'טים מלמעלה למטה.
לדוגמה, כשגודל אזור התצוגה משתנה:
התהליך הזה לא מיידי, ולכן המאפיינים החזותיים המשוכפלים כוללים גם אסימון סנכרון. המאגר של Viz משתמש בטוקן הסנכרון הזה כדי להמתין עד שכל החלקים המקומיים של עץ הפריימים ישלחו פריים של המאגר עם טוקן הסנכרון הנוכחי. התהליך הזה מונע ערבוב של פריימים של מעבד הקומפוזיציה עם מאפיינים חזותיים שונים.
עץ המקטעים שלא ניתן לשינוי
עץ הפסקאות הקבוע הוא הפלט של שלב הפריסה בצינור עיבוד הנתונים לעיבוד. הוא מייצג את המיקום והגודל של כל הרכיבים בדף (ללא טרנספורמציות).
כל קטע מייצג חלק מאלמנט DOM. בדרך כלל יש רק קטע אחד לכל רכיב, אבל יכולים להיות יותר קטעים אם הוא מפוצל בין דפים שונים בזמן ההדפסה, או בין עמודות בהקשר של כמה עמודות.
אחרי הפריסה, כל מקטע הופך לבלתי ניתן לשינוי ולא משתנה שוב. חשוב לציין גם כמה הגבלות נוספות. אנחנו לא:
- מתן הרשאה לכל הפניות 'מעלה' בעץ. (לצאצא לא יכול להיות מצביע להורה שלו).
- 'להעביר' נתונים במורד העץ (צאצא קורא מידע רק מהצאצאים שלו, ולא מההורה).
ההגבלות האלה מאפשרות לנו לעשות שימוש חוזר בחלקיק לפריסה הבאה. בלי ההגבלות האלה, נצטרך לעתים קרובות ליצור מחדש את כל העץ, וזה יקר.
בדרך כלל, רוב הפריסות הן עדכונים מצטברים. לדוגמה, אפליקציית אינטרנט שמעדכנת חלק קטן מממשק המשתמש בתגובה ללחיצה של המשתמש על רכיב. באופן אידיאלי, הפריסה צריכה לפעול רק באופן יחסי למה שהשתנה בפועל במסך. כדי לעשות זאת, אנחנו משתמשים מחדש בכמה שיותר חלקים מהעץ הקודם. כלומר (בדרך כלל) אנחנו צריכים לבנות מחדש את עמוד השדרה של העץ.
בעתיד, העיצוב הבלתי משתנה הזה יוכל לאפשר לנו לעשות דברים מעניינים, כמו העברת עץ המקטע שלא ניתן לשינוי דרך גבולות השרשור במקרה הצורך (כדי לבצע את השלבים הבאים בשרשור אחר), ליצור מספר עצים לאנימציה של פריסה חלקה, או לבצע פריסות ספקולטיביות מקבילות. הוא גם מאפשר לנו להשתמש בתצוגה עם כמה שרשורים (multi-threading).
פריטי מקטע מוטבעים
תוכן בתוך שורה (בעיקר טקסט עם עיצוב) מוצג בצורה שונה במקצת. במקום מבנה עץ עם תיבות ומצביעים, אנחנו מייצגים תוכן בתוך שורה ברשימת שטוח שמייצגת את העץ. היתרון העיקרי הוא שמייצג רשימה שטוח של שורות מוטמעות הוא מהיר, שימושי לבדיקה או לשאילתות על מבני נתונים מוטמעים, ויעיל מבחינת שימוש בזיכרון. זה חשוב מאוד לביצועים של עיבוד הנתונים באינטרנט, כי עיבוד טקסט הוא תהליך מורכב מאוד, שיכול בקלות להפוך לחלק האיטי ביותר בצינור עיבוד הנתונים, אם לא מבצעים אופטימיזציה משמעותית.
הרשימה הרגילה נוצרת לכל הקשר של עיצוב בתוך שורה לפי סדר החיפוש לעומק של עץ המשנה של הפריסה בתוך השורה. כל ערך ברשימה הוא מספר (אובייקט, מספר צאצאים). לדוגמה, ה-DOM הזה:
<div style="width: 0;">
<span style="color: blue; position: relative;">Hi</span> <b>there</b>.
</div>
המאפיין width
מוגדר ל-0 כך שהקו עובר בין 'Hi' ו-'there'.
כשהקשר של העיצוב בתוך השורה למקרה הזה מיוצג כעץ, הוא נראה כך:
{
"Line box": {
"Box <span>": {
"Text": "Hi"
}
},
"Line box": {
"Box <b>": {
"Text": "There"
}
},
{
"Text": "."
}
}
הרשימה הרגילה נראית כך:
- (קופסת שורה, 2)
- (Box <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) משתמשים בעצי מאפיין למטרות רבות, כולל:
- הפרדת הרכבה מצביעה, והרכבה מהשרשור הראשי.
- קביעת אסטרטגיית הרכבה או ציור אופטימלית.
- מדידת הגיאומטריה של IntersectionObserver.
- הימנעות מעבודה על אלמנטים מחוץ למסך ועל משבצות של טקסטורות של GPU.
- ביטול התוקף של צבע ורסטר באופן יעיל ומדויק.
- מדידת הזזת הפריסה והצגת התוכן הכי גדול (LCP) בדוח המדדים הבסיסיים של חוויית המשתמש באתר.
לכל מסמך אינטרנט יש ארבעה עצי מאפיינים נפרדים: טרנספורמציה, חיתוך, אפקט וגלילה.(*) עץ הטרנספורמציה מייצג המרות וגלילה ב-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 כדי ליצור רשימה של פריטים להצגה.
לדוגמה:
<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 והטקסט "שלום עולם". |
רשימת הפריטים בתצוגה מסודרת משני הכיוונים. בדוגמה שלמעלה, ה-div הירוק מופיע לפני ה-div הכחול בסדר DOM, אבל סדר הצבע של ה-CSS מחייב שה- div הכחול ב-z-index לפני ה-div הירוק (שלב 4.1). פריטים בתצוגה תואמים בקירוב לשלבים אטומיים במפרט הזמנת הצבע של שירות ה-CSS. רכיב DOM אחד יכול להוביל לכמה פריטים בתצוגה, למשל, ל-#green יש פריט תצוגה לרקע ופריט תצוגה נוסף לטקסט בשורה. רמת הפירוט הזו חשובה לייצוג המורכבות המלאה של מפרט הזמנת הצבע ב-CSS, למשל שילוב התוכן שנוצר על ידי שוליים שליליים:
<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'. |
עץ מאפייני הטרנספורמציה וקטעי הציור יהיו (בקיצור):
הרשימה הממוזערת של קטעי הציור, שהם קבוצות של פריטים מוצגים ומצב של עץ נכסים, היא הקלט לשלב חלוקת השכבות בצינור עיבוד הנתונים לעיבוד. אפשר למזג את כל הרשימה של מקטעי הצבע לשכבה מורכבת אחת וליצור רסטר יחד, אבל יהיה צורך לבצע עיבוד רסטר בכל פעם שהמשתמש גולל. אפשר ליצור שכבה מורכבת לכל מקטע צביעה ולבצע רסטרציה בנפרד כדי להימנע מביצוע רסטרציה מחדש, אבל הפעולה הזו תגרום לזיכרון ה-GPU להתרוקן במהירות. בשלב יצירת השכבות צריך למצוא איזון בין זיכרון ה-GPU לבין צמצום העלויות כשהדברים משתנים. גישה כללית טובה היא למזג קטעי קוד כברירת מחדל, ולא למזג קטעי קוד של ציור שיש להם מצבים של עץ נכסים שצפויים להשתנות בשרשור של המאגר, כמו גלילה בשרשור של המאגר או אנימציות של טרנספורמציה בשרשור של המאגר.
באופן אידיאלי, הדוגמה הקודמת אמורה ליצור שתי שכבות מורכבות:
- שכבת קומפוזיציה בגודל 800x600 שמכילה את פקודות הציור:
drawRect
בגודל 800x600 ובצבע לבןdrawRect
בגודל 100x100 במיקום 0,0 ובצבע ורוד
- שכבת קומפוזיציה בגודל 144x224 שמכילה את פקודות הציור:
drawTextBlob
עם המיקום 0,0 והטקסט 'Hello world'- translate 0,18
rotateZ(25deg)
drawRect
בגודל 75x200 במיקום 0,0 ובצבע כתוםdrawTextBlob
עם המיקום 0,0 והטקסט 'I'm falling'
אם המשתמש גולל ב-#scroll
, השכבה המורכבת השנייה מועברת, אבל לא צריך ליצור רסטר.
בדוגמה, מהקטע הקודם על עצי נכסים, יש שישה קטעי צבע. ביחד עם מצבי עץ המאפיין (טרנספורמציה, קליפ, אפקט, גלילה) הם:
- רקע המסמך: גלילה במסמך, קליפ, שורש, גלילת מסמך.
- פינה אופקית, פינה אנכית ופינה של גלילה ל-div (שלושה קטעי צביעה נפרדים): גלילה במסמך, חיתוך של מסמך, טשטוש
#one
, גלילה במסמך. - iframe
#one
:#one
סיבוב, קליפ גלילה לאפשרויות נוספות,#one
טשטוש, גלילה ב-div. - iframe
#two
: קנה מידה של#two
, קליפ של מסמך, בסיס, גלילה במסמך.
פריימים של קומפוזיציה: משטחים, משטחים, משטחים ומשבצות טקסטורה של GPU
תהליכי הדפדפן והעיבוד מנהלים רסטריזציה של תוכן, ואז שולחים פריימים של קומפוזיציה לתהליך Viz כדי להציג אותם במסך. פריימים של קומפוזיציה מייצגים איך לחבר תוכן מעובד לרשת ולצייר אותו ביעילות באמצעות ה-GPU.
כרטיס מידע
בתיאוריה, מעבד תהליך עיבוד או מעבד תהליך דפדפן יכולים לבצע רסטורציה של פיקסלים לתוך טקסטורה אחת בגודל מלא של חלון התצוגה של ה-renderer, ולשלוח את הטקסטורה הזו ל-Viz. כדי להציג אותה, מעבד התצוגה יצטרך רק להעתיק את הפיקסלים מהטקסטורה היחידה הזו למיקום המתאים במאגר המסגרות (לדוגמה, המסך). עם זאת, אם המרכיב ירצו לעדכן אפילו פיקסל אחד, הוא יצטרך לרסטר מחדש את אזור התצוגה המלא ולשלוח טקסטורה חדשה ל-Viz.
במקום זאת, אזור התצוגה מחולק לריבועים. כל משבצת של טקסטורה ב-GPU תומכת בפיקסלים הרסטריפיים של חלק משטח התצוגה של כל משבצת. לאחר מכן, ה-renderer יכול לעדכן משבצות בודדות או אפילו רק לשנות את המיקום במסך של המשבצות הקיימות. לדוגמה, כשגוללים באתר, המיקום של המשבצות הקיימות משתנה למעלה ורק מדי פעם צריך לעבד משבצת חדשה כדי להציג תוכן שנמצא בחלק התחתון של הדף.
ריבועים ומשטחים
משבצות של טקסטורות של GPU הן סוג מיוחד של quad, שהוא פשוט שם מעוצב לקטגוריה מסוימת של טקסטורה. פוליגון מרובע מזהה את מרקם הקלט, ומציין איך לבצע עליו טרנספורמציה ולהחיל עליו אפקטים חזותיים. לדוגמה, למשבצות תוכן רגילות יש טרנספורמציה שמציינת את המיקום שלהן ב-x ו-y ברשת המשבצות.
המשבצות הרסטרליות האלה עטופות בתהליך רינדור, שהוא רשימה של ריבועים. תהליך ה-render pass לא מכיל מידע על פיקסלים, אלא הוראות לגבי המיקום והאופן שבהם צריך לצייר כל ריבוע כדי ליצור את פלט הפיקסלים הרצוי. יש draw quad לכל משבצת של טקסטורה ב-GPU. מחבר התצוגה צריך רק לבצע איטרציה ברשימת הריבועים, תוך ציור של כל אחת עם האפקטים החזותיים שצוינו, כדי להפיק את פלט הפיקסלים הרצוי למעבר העיבוד. אפשר לבצע שילוב של ריבועים מצוירים לצורך שלב עיבוד (pass) ברנדור ביעילות ב-GPU, כי האפקטים החזותיים המותרים נבחרים בקפידה כך שיתאימו למיפוי ישיר לתכונות של ה-GPU.
יש סוגים נוספים של שורות משיכה מעבר למשבצות רסטר. לדוגמה, יש ריבועי ציור בצבע אחיד שלא מבוססים על טקסטורה בכלל, או ריבועי ציור עם טקסטורה לטקסטורות שאינן משבצות, כמו וידאו או קנבס.
אפשר גם להטמיע מסגרת של מעבד תמונות במסגרת אחרת של מעבד תמונות. לדוגמה, קומפוזיציה של הדפדפן מייצר מסגרת קומפוזיציה עם ממשק המשתמש של הדפדפן, ומלבן ריק שבו יוטמע התוכן של קומפוזבילי עיבוד. דוגמה נוספת היא מסגרות iframe מבודדות מאתרים. ההטמעה הזו מתבצעת באמצעות משטחים.
כאשר קומפוזיציה שולח מסגרת קומפוזיציה, הוא מלווה במזהה, שנקרא מזהה שטח, שמאפשר למסגרות של קומפוזביליות אחרות להטמיע אותה לפי הפניה. Viz שומר את מסגרת המאגר העדכנית ביותר שנשלחה עם מזהה פלטפורמה מסוים. מסגרת מאגר אחרת יכולה להפנות אליה מאוחר יותר באמצעות surface draw quad, ולכן Viz יודע מה לצייר. (לתשומת ליבכם: quads שרטוט על פני השטח מכילים רק מזהי משטחים, ולא מרקמים).
אישורים לעיבוד ביניים
כדי להשתמש בחלק מהאפקטים החזותיים, כמו פילטרים או מצבי שילוב מתקדמים, צריך למשוך שתי ריבועים או יותר למרקם ביניים. לאחר מכן, הטקסטורה הביניים נמשכת למאגר יעד ב-GPU (או לטקסטורה ביניים אחרת), תוך החלת האפקט החזותי בו-זמנית. כדי לאפשר זאת, מסגרת קומפוזיציה מכילה למעשה רשימה של אישורי רינדור. תמיד יש שלב עיבוד גרפי ברמה הבסיסית (root), שהוא האחרון שמצויר והיעד שלו תואם למאגר המסגרות. יכול להיות שיהיו עוד שלבים.
האפשרות לבצע כמה שלבי רינדור היא הסיבה לשם 'שלב רינדור'. צריך להריץ כל כרטיס ברצף ב-GPU, במספר 'כרטיסים', אבל אפשר להשלים כרטיס אחד בחישוב של GPU אחד מקביל וחריף.
צבירה
מספר פריימים של מעבד התמונות נשלחים ל-Viz, וצריך לצייר אותם יחד במסך. לשם כך, מתבצעת שלב צבירת נתונים שממיר אותם לפריים מורכב אחד של מעבד התמונות. במסגרת הצבירה, המערכת מחליפה את הריבועים של ציור פני השטח במסגרות של המאגר שהן מציינות. זו גם הזדמנות לבצע אופטימיזציה של טקסטורות ביניים או תוכן מיותר שלא מופיע במסך. לדוגמה, במקרים רבים, למסגרת המאגר של רכיב ה-iframe המבודד באתר לא נדרש מרקם ביניים משלו, וניתן לצייר אותו ישירות במאגר המסגרות באמצעות ריבועיות ציור מתאימות. שלב הצבירה קובע אופטימיזציות כאלה ומיישם אותן על סמך ידע גלובלי שאינו נגיש למחבר אחד של רינדור.
דוגמה
אלה פריימים של הקומפוזיציה שמייצגים את הדוגמה מתחילת הפוסט הזה.
- הפלטפורמה
foo.com/index.html
: id=0- תהליך עיבוד גרפי 0: ציור לפלט.
- ציור של ריבוע ב-render pass: ציור עם טשטוש של 3px וקיצוץ ל-render pass 0.
- תהליך עיבוד גרפי 1:
- ציור של ריבועים לתוכן המשבצות של iframe
#one
, עם מיקומי x ו-y לכל אחד מהם.
- ציור של ריבועים לתוכן המשבצות של iframe
- תהליך עיבוד גרפי 1:
- משטח ציור quad: עם מזהה 2, מצויר עם קנה מידה ותרגום של טרנספורמציה.
- ציור של ריבוע ב-render pass: ציור עם טשטוש של 3px וקיצוץ ל-render pass 0.
- תהליך עיבוד גרפי 0: ציור לפלט.
- ממשק המשתמש בדפדפן: ID=1
- תהליך עיבוד גרפי 0: ציור לפלט.
- ציור ריבועים לממשק המשתמש בדפדפן (גם בחלוקה לריבועים)
- תהליך עיבוד גרפי 0: ציור לפלט.
bar.com/index.html
surface: ID=2- תהליך עיבוד גרפי 0: ציור לפלט.
- משרטטים מרובעים לתוכן של
#two
iframe, כאשר מיקומי x ו-y בכל אחד מהם.
- משרטטים מרובעים לתוכן של
- תהליך עיבוד גרפי 0: ציור לפלט.
איורים של Una Kravets.