มาดูโครงสร้างข้อมูลหลักกัน ซึ่งเป็นอินพุตและเอาต์พุตของไปป์ไลน์การแสดงผล
โครงสร้างข้อมูลเหล่านี้ ได้แก่
- เฟรมทรีประกอบด้วยโหนดในเครื่องและโหนดระยะไกลที่แสดงถึงเว็บเอกสารที่อยู่ในกระบวนการแสดงผลและโปรแกรมแสดงผล 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 อาจเลือกที่จะแสดงผลเฟรมข้ามแหล่งที่มาในกระบวนการแสดงผลที่แตกต่างจากเฟรมหลัก
ในตัวอย่างนี้ มีเฟรมทั้งหมด 3 เฟรม ดังนี้
เมื่อใช้การแยกเว็บไซต์ Chromium จะใช้กระบวนการแสดงผล 2 รายการเพื่อแสดงผลหน้าเว็บนี้ กระบวนการแสดงผลแต่ละรายการมีการแสดงผลของต้นไม้เฟรมสําหรับหน้าเว็บนั้นๆ ดังนี้
เฟรมที่แสดงผลในกระบวนการอื่นจะแสดงเป็นเฟรมระยะไกล เฟรมระยะไกลจะเก็บข้อมูลขั้นต่ำที่จำเป็นเพื่อใช้เป็นตัวยึดตําแหน่งในการแสดงผล เช่น ขนาด มิเช่นนั้นเฟรมระยะไกลจะไม่มีข้อมูลที่จำเป็นในการแสดงผลเนื้อหาจริง
ในทางตรงกันข้าม เฟรมในเครื่องแสดงถึงเฟรมที่ผ่านไปป์ไลน์การแสดงผลมาตรฐาน เฟรมในเครื่องมีข้อมูลทั้งหมดที่จำเป็นในการเปลี่ยนข้อมูลของเฟรมนั้น (เช่น ต้นไม้ DOM และข้อมูลสไตล์) ให้เป็นสิ่งที่แสดงผลได้
ไปป์ไลน์การแสดงผลจะทำงานตามความละเอียดของเศษส่วนของต้นไม้เฟรมในเครื่อง
ลองดูตัวอย่างที่ซับซ้อนขึ้นซึ่งมี foo.com
เป็นเฟรมหลัก
<iframe src="bar.com"></iframe>
และเฟรมย่อย bar.com
ต่อไปนี้
<iframe src="foo.com/etc"></iframe>
แม้ว่าจะมีโปรแกรมแสดงผลเพียง 2 รายการ แต่ตอนนี้ก็มีเศษส่วนของเฟรมเทรียร์ในเครื่อง 3 รายการ โดย 2 รายการอยู่ในกระบวนการแสดงผลสำหรับ foo.com
และ 1 รายการอยู่ในกระบวนการแสดงผลสำหรับ bar.com
ดังนี้
หากต้องการสร้างเฟรมคอมโพสิต 1 เฟรมสําหรับหน้าเว็บ วิซจะขอเฟรมคอมโพสิตจากเฟรมรูทของต้นไม้เฟรมในเครื่อง 3 ต้นพร้อมกัน จากนั้นจะรวบรวมเฟรมเหล่านั้น โปรดดูส่วนเฟรมคอมโพสิตด้วย
foo.com
เฟรมหลักและเฟรมย่อย foo.com/other-page
เป็นส่วนหนึ่งของต้นไม้เฟรมเดียวกันและแสดงผลในกระบวนการเดียวกัน
อย่างไรก็ตาม เฟรม 2 เฟรมนี้ยังคงมีวงจรชีวิตของเอกสารแยกกันเนื่องจากเป็นส่วนหนึ่งของเศษส่วนของต้นไม้เฟรมในเครื่องที่แตกต่างกัน
ด้วยเหตุนี้ คุณจึงสร้างเฟรมคอมโพสิต 1 เฟรมสำหรับทั้ง 2 รายการในการอัปเดตครั้งเดียวไม่ได้
กระบวนการแสดงผลมีข้อมูลไม่เพียงพอที่จะคอมโพสเฟรมคอมโพสิตที่สร้างสำหรับ foo.com/other-page
ลงในเฟรมคอมโพสิตสำหรับเฟรมหลัก foo.com
โดยตรง
เช่น เฟรมหลัก bar.com
ที่อยู่นอกกระบวนการอาจส่งผลต่อการแสดงผลของ iframe foo.com/other-url
โดยการเปลี่ยนรูปแบบ iframe ด้วย CSS หรือบดบังบางส่วนของ iframe ด้วยองค์ประกอบอื่นๆ ใน DOM
Waterfall การอัปเดตพร็อพเพอร์ตี้ภาพ
พร็อพเพอร์ตี้ภาพ เช่น ปัจจัยที่มีผลต่อขนาดการแสดงผลของอุปกรณ์และขนาดวิวพอร์ต จะส่งผลต่อเอาต์พุตที่แสดงผล และจะต้องซิงค์กันระหว่างเศษส่วนของต้นไม้เฟรมในเครื่อง รูทของเศษส่วนของต้นไม้เฟรมในเครื่องแต่ละรายการจะมีออบเจ็กต์วิดเจ็ตที่เชื่อมโยงอยู่ การอัปเดตพร็อพเพอร์ตี้ภาพจะไปยังวิดเจ็ตของเฟรมหลักก่อนที่จะนำไปใช้กับวิดเจ็ตที่เหลือจากบนลงล่าง
ตัวอย่างเช่น เมื่อขนาดวิวพอร์ตเปลี่ยนแปลง
กระบวนการนี้ไม่ได้เกิดขึ้นทันที ดังนั้นพร็อพเพอร์ตี้ภาพแบบทำซ้ำจึงมีโทเค็นการซิงค์ด้วย โปรแกรมผสมภาพใช้โทเค็นการซิงค์นี้เพื่อรอให้เศษส่วนของต้นไม้เฟรมในเครื่องทั้งหมดส่งเฟรมโปรแกรมผสมภาพที่มีโทเค็นการซิงค์ปัจจุบัน กระบวนการนี้จะช่วยหลีกเลี่ยงการผสมเฟรมคอมโพสิตกับพร็อพเพอร์ตี้ภาพที่แตกต่างกัน
ต้นไม้ที่เปลี่ยนแปลงไม่ได้ของข้อมูลโค้ด
ต้นไม้เศษข้อมูลที่เปลี่ยนแปลงไม่ได้คือเอาต์พุตของระยะเลย์เอาต์ของไปป์ไลน์การแสดงผล ซึ่งแสดงตําแหน่งและขนาดขององค์ประกอบทั้งหมดในหน้า (โดยไม่ใช้การเปลี่ยนรูปแบบ)
แต่ละส่วนแสดงถึงองค์ประกอบ DOM บางส่วน โดยปกติแล้วจะมีเพียง 1 รายการต่อองค์ประกอบ 1 รายการ แต่อาจมีมากกว่านี้หากมีการแบ่งออกเป็นหน้าต่างๆ เมื่อพิมพ์ หรือแบ่งออกเป็นคอลัมน์ต่างๆ เมื่ออยู่ในบริบทแบบหลายคอลัมน์
หลังจากวางเลย์เอาต์แล้ว แต่ละส่วนจะเปลี่ยนแปลงไม่ได้และจะไม่มีการเปลี่ยนแปลงอีก และที่สำคัญ เรายังมีข้อจำกัดเพิ่มเติมอีก 2-3 ข้อ เราไม่ทำสิ่งต่อไปนี้
- อนุญาตการอ้างอิง "ขึ้น" ในลําดับชั้น (รายการย่อยจะไม่มีพอยน์เตอร์ไปยังรายการหลัก)
- "ขยาย" ข้อมูลลงในลําดับชั้น (โหนดย่อยจะอ่านข้อมูลจากโหนดย่อยเท่านั้น ไม่ใช่จากโหนดหลัก)
ข้อจํากัดเหล่านี้ช่วยให้เรานําข้อมูลโค้ดที่ตัดออกมาใช้ซ้ำในเลย์เอาต์ต่อๆ ไปได้ หากไม่มีข้อจำกัดเหล่านี้ เราจะต้องสร้างต้นไม้ทั้งต้นขึ้นมาใหม่บ่อยครั้ง ซึ่งเป็นเรื่องที่มีค่าใช้จ่ายสูง
โดยปกติแล้ว เลย์เอาต์ส่วนใหญ่เป็นการอัปเดตแบบเพิ่มทีละน้อย เช่น เว็บแอปที่อัปเดต UI เพียงส่วนเล็กๆ เพื่อตอบสนองต่อการที่ผู้ใช้คลิกองค์ประกอบ ตามหลักการแล้ว เลย์เอาต์ควรทํางานตามสัดส่วนกับสิ่งที่เปลี่ยนแปลงบนหน้าจอจริงเท่านั้น ซึ่งทำได้ด้วยการนําส่วนต่างๆ ของโครงสร้างเดิมมาใช้ซ้ำมากที่สุด ซึ่งหมายความว่า (โดยทั่วไป) เราจําเป็นต้องสร้างกระดูกสันหลังของต้นไม้ขึ้นมาใหม่เท่านั้น
ในอนาคต การออกแบบแบบคงที่นี้อาจช่วยให้เราทําสิ่งที่น่าสนใจได้ เช่น ส่งผ่านต้นไม้ของชิ้นส่วนแบบคงที่ข้ามขอบเขตของเธรด หากจําเป็น (เพื่อดําเนินการระยะต่อๆ ไปในเธรดอื่น) สร้างต้นไม้หลายต้นเพื่อให้ภาพเคลื่อนไหวของเลย์เอาต์ราบรื่น หรือดําเนินการเลย์เอาต์แบบคาดการณ์แบบขนาน นอกจากนี้ เรายังใช้เลย์เอาต์แบบหลายเธรดได้อีกด้วย
รายการข้อมูลโค้ดที่ติดทั่วเว็บไซต์
เนื้อหาในบรรทัด (ข้อความที่มีการจัดรูปแบบเป็นหลัก) ใช้การแสดงผลที่แตกต่างออกไปเล็กน้อย เราแสดงเนื้อหาในบรรทัดในรายการแบบแบนแทนโครงสร้างแบบต้นไม้ที่มีกล่องและเคอร์เซอร์ ประโยชน์หลักคือการแสดงรายการแบบแบนสำหรับอินไลน์นั้นรวดเร็ว มีประโยชน์ในการตรวจสอบหรือค้นหาโครงสร้างข้อมูลในบรรทัด และประหยัดหน่วยความจำ การดำเนินการนี้สำคัญอย่างยิ่งต่อประสิทธิภาพการแสดงผลของเว็บ เนื่องจากการแสดงผลข้อความมีความซับซ้อนมาก และอาจกลายเป็นส่วนที่ช้าที่สุดของไปป์ไลน์ได้ง่ายๆ เว้นแต่จะได้รับการเพิ่มประสิทธิภาพอย่างมาก
ระบบจะสร้างรายการแบบแบนสำหรับบริบทการจัดรูปแบบในบรรทัดแต่ละรายการตามลําดับการค้นหาแบบเจาะลึกของต้นไม้ย่อยของเลย์เอาต์ในบรรทัด รายการแต่ละรายการในลิสต์คือทูเปิลของ (ออบเจ็กต์, จํานวนรายการสืบทอด) ตัวอย่างเช่น ลองดู 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)
- (กล่อง <b>, 1)
- (ข้อความ "there", 0)
- (ข้อความ ".", 0)
โครงสร้างข้อมูลนี้มีผู้ใช้จำนวนมาก เช่น API การช่วยเหลือพิเศษ และ API เรขาคณิต เช่น getClientRects
และ contenteditable
โดยแต่ละรายการมีข้อกำหนดที่แตกต่างกัน
คอมโพเนนต์เหล่านี้เข้าถึงโครงสร้างข้อมูลที่เรียบง่ายผ่านเคอร์เซอร์ที่สะดวก
เคอร์เซอร์มี API เช่น MoveToNext
, MoveToNextLine
, CursorForChildren
การนําเสนอเคอร์เซอร์นี้มีประสิทธิภาพมากสําหรับเนื้อหาข้อความเนื่องด้วยเหตุผลหลายประการ ดังนี้
- การทำซ้ำตามลําดับการค้นหาแบบเข้าหาลําดับชั้นจะทําได้อย่างรวดเร็ว ซึ่งใช้กันบ่อยมากเนื่องจากคล้ายกับการเคลื่อนไหวเคอร์เซอร์ เนื่องจากเป็นรายการแบบแบน การค้นหาจากระดับลึกจึงเพิ่มเฉพาะออฟเซตของอาร์เรย์เท่านั้น ซึ่งทำให้การวนซ้ำรวดเร็วและมีการเข้าถึงหน่วยความจำในบริเวณใกล้เคียง
- ซึ่งจะทำการค้นหาแบบกว้างก่อน ซึ่งจำเป็นในกรณีต่างๆ เช่น การวาดพื้นหลังของกล่องบรรทัดและคอลัมน์ในบรรทัด
- การทราบจํานวนรายการที่สืบทอดจะช่วยให้ไปยังรายการถัดไปได้อย่างรวดเร็ว (เพียงเพิ่มออฟเซตของอาร์เรย์ตามจํานวนนั้น)
โครงสร้างพร็อพเพอร์ตี้
DOM คือต้นไม้ขององค์ประกอบ (รวมถึงโหนดข้อความ) และ CSS สามารถใช้รูปแบบต่างๆ กับองค์ประกอบได้
ซึ่งปรากฏได้ 4 วิธีดังนี้
- เลย์เอาต์: อินพุตสำหรับอัลกอริทึมข้อจำกัดของเลย์เอาต์
- Paint: วิธีวาดและแรสเตอร์องค์ประกอบ (แต่ไม่ใช่องค์ประกอบย่อย)
- ภาพ: เอฟเฟกต์แรสเตอร์/วาดที่ใช้กับ DOM ย่อย เช่น การเปลี่ยนรูปแบบ ฟิลเตอร์ และการครอบตัด
- การเลื่อน: มุมที่ปัดเศษและปรับแนวตามแกน การครอบตัดและการเลื่อนของแผนผังย่อยที่รวมอยู่
ต้นไม้พร็อพเพอร์ตี้คือโครงสร้างข้อมูลที่อธิบายว่าเอฟเฟกต์ภาพและการเลื่อนมีผลกับองค์ประกอบ DOM อย่างไร องค์ประกอบเหล่านี้เป็นวิธีตอบคําถามต่างๆ เช่น องค์ประกอบ DOM หนึ่งๆ อยู่ตรงไหนเมื่อเทียบกับหน้าจอ โดยพิจารณาจากขนาดและตําแหน่งของเลย์เอาต์ และควรใช้ลําดับการดําเนินการของ GPU ใดเพื่อใช้เอฟเฟกต์ภาพและการเลื่อน
เอฟเฟกต์ภาพและการเลื่อนบนเว็บมีความซับซ้อนมาก ดังนั้น สิ่งที่สําคัญที่สุดที่ทรีพร็อพเพอร์ตี้ทําคือเปลี่ยนความซับซ้อนนั้นให้กลายเป็นโครงสร้างข้อมูลที่แสดงถึงโครงสร้างและความหมายอย่างแม่นยํา ในขณะเดียวกันก็นําความซับซ้อนที่เหลือของ DOM และ CSS ออก ซึ่งช่วยให้เราใช้อัลกอริทึมสำหรับการวางซ้อนและเลื่อนได้อย่างมั่นใจมากขึ้น โดยเฉพาะอย่างยิ่งฟีเจอร์ต่อไปนี้
- คุณสามารถรวบรวมเรขาคณิตและการคำนวณอื่นๆ ที่อาจทำให้เกิดข้อผิดพลาดไว้ในที่เดียว
- ความซับซ้อนของการสร้างและการอัปเดตลําดับชั้นพร็อพเพอร์ตี้จะแยกออกเป็นขั้นตอนไปป์ไลน์การแสดงผล 1 ขั้นตอน
- การส่งทรีพร็อพเพอร์ตี้ไปยังเธรดและกระบวนการต่างๆ นั้นง่ายและเร็วกว่าสถานะ DOM แบบสมบูรณ์มาก จึงทําให้นําไปใช้กับกรณีการใช้งานได้หลายกรณี
- ยิ่งมีกรณีการใช้งานมากเท่าใด เราก็ยิ่งได้รับประโยชน์มากขึ้นจากการแคชรูปทรงเรขาคณิตที่สร้างขึ้นเพิ่มเติม เนื่องจากสามารถนําแคชของกันและมาใช้ซ้ำได้
RenderingNG ใช้ต้นไม้พร็อพเพอร์ตี้เพื่อวัตถุประสงค์หลายอย่าง ซึ่งรวมถึง
- การแยกการคอมโพสออกจากการวาด และการคอมโพสออกจากเธรดหลัก
- การกำหนดกลยุทธ์การคอมโพส / วาดภาพที่เหมาะสมที่สุด
- การวัดเรขาคณิตของ IntersectionObserver
- หลีกเลี่ยงการประมวลผลองค์ประกอบที่อยู่นอกหน้าจอและชิ้นส่วนพื้นผิว GPU
- ลบล้างข้อมูล Paint และแรสเตอร์อย่างมีประสิทธิภาพและถูกต้อง
- การวัดการเปลี่ยนเลย์เอาต์และ Largest Contentful Paint ใน Core Web Vitals
เอกสารเว็บทุกฉบับมีต้นไม้พร็อพเพอร์ตี้แยกกัน 4 รายการ ได้แก่ การเปลี่ยนรูปแบบ คลิป เอฟเฟกต์ และการเลื่อน(*) ต้นไม้การเปลี่ยนรูปแบบแสดงการเปลี่ยนรูปแบบ CSS และการเลื่อน (การเปลี่ยนรูปแบบการเลื่อนจะแสดงเป็นเมตริกซ์การเปลี่ยนรูปแบบ 2 มิติ) ต้นไม้คลิปแสดงคลิปที่เกินขีดจำกัด แผนภาพเอฟเฟกต์แสดงเอฟเฟกต์ภาพอื่นๆ ทั้งหมด ได้แก่ ความทึบแสง ฟิลเตอร์ มาสก์ โหมดการผสม และคลิปประเภทอื่นๆ เช่น clip-path ต้นไม้การเลื่อนแสดงข้อมูลเกี่ยวกับการเลื่อน เช่น วิธีที่การเลื่อนเชื่อมโยงกัน ซึ่งจำเป็นต้องใช้สำหรับการเลื่อนในเธรดคอมโพสิต โหนดแต่ละโหนดในต้นไม้พร็อพเพอร์ตี้แสดงการเลื่อนหรือเอฟเฟกต์ภาพที่ใช้โดยองค์ประกอบ DOM หากองค์ประกอบมีเอฟเฟกต์หลายรายการ อาจมีโหนดต้นไม้พร็อพเพอร์ตี้มากกว่า 1 โหนดในแต่ละต้นไม้สำหรับองค์ประกอบเดียวกัน
โทโพโลยีของต้นไม้แต่ละต้นจะคล้ายกับการแสดง DOM แบบเบาบาง ตัวอย่างเช่น หากมีองค์ประกอบ DOM 3 รายการที่มีคลิปที่แสดงเกิน บริบทของคลิปจะมีโหนดต้นไม้คลิป 3 รายการ และโครงสร้างของต้นไม้คลิปจะเป็นไปตามความสัมพันธ์ของบล็อกที่บรรจุระหว่างคลิปที่แสดงเกิน นอกจากนี้ยังมีลิงก์ระหว่างต้นไม้ด้วย ลิงก์เหล่านี้ระบุลําดับชั้น DOM แบบสัมพัทธ์ของโหนด และลําดับการใช้ เช่น หากการเปลี่ยนรูปแบบในองค์ประกอบ DOM อยู่ใต้องค์ประกอบ DOM อื่นที่มีตัวกรอง แน่นอนว่าการเปลี่ยนรูปแบบจะมีผลก่อนตัวกรอง
องค์ประกอบ DOM แต่ละรายการมีสถานะต้นไม้พร็อพเพอร์ตี้ ซึ่งเป็นชุด 4 รายการ (การเปลี่ยนรูปแบบ คลิป เอฟเฟกต์ เลื่อน) ที่ระบุคลิป การเปลี่ยนรูปแบบ และโหนดต้นไม้เอฟเฟกต์ของบรรพบุรุษที่อยู่ใกล้ที่สุดซึ่งมีผลกับองค์ประกอบนั้น ซึ่งสะดวกมากเนื่องจากข้อมูลนี้ช่วยให้เราทราบรายการคลิป การเปลี่ยนรูปแบบ และเอฟเฟกต์ที่ใช้กับองค์ประกอบนั้นๆ และลำดับที่แน่นอน ซึ่งบอกตำแหน่งบนหน้าจอและวิธีวาด
ตัวอย่าง
(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>
สําหรับตัวอย่างก่อนหน้านี้ (ซึ่งแตกต่างจากตัวอย่างในส่วนนําเข้าเล็กน้อย) องค์ประกอบหลักของต้นไม้พร็อพเพอร์ตี้ที่สร้างขึ้นมีดังนี้
แสดงรายการและพาร์ท Paint
รายการที่แสดงมีคำสั่งการวาดระดับต่ำ (ดูที่นี่) ที่แรสเตอร์ได้โดยใช้ 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 และข้อความ "Hello world" |
รายการรายการที่แสดงจะเรียงจากหลังไปหน้า ในตัวอย่างด้านบน div สีเขียวอยู่ก่อน div สีน้ำเงินตามลําดับ DOM แต่ลําดับการวาดของ CSS กําหนดให้ div สีน้ำเงินที่มี z-index ติดลบต้องวาดก่อน (ขั้นตอนที่ 3) 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" |
ระบบจะจัดเก็บรายการสินค้าที่จะแสดงไว้และนํากลับมาใช้ใหม่ในการอัปเดตในภายหลัง หากออบเจ็กต์เลย์เอาต์ไม่มีการเปลี่ยนแปลงระหว่างการเรียกใช้ต้นไม้ Paint ระบบจะคัดลอกรายการที่แสดงจากรายการก่อนหน้า การเพิ่มประสิทธิภาพเพิ่มเติมจะอาศัยพร็อพเพอร์ตี้ของข้อกําหนดเฉพาะลําดับการแสดงผล 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 และข้อความ "ฉันกำลังตก" |
ต้นไม้พร็อพเพอร์ตี้การเปลี่ยนรูปแบบและกลุ่มการวาดภาพจะเป็นดังนี้ (ย่อเพื่อความกระชับ)
รายการที่เรียงลำดับของข้อมูลการวาด ซึ่งก็คือกลุ่มของรายการที่แสดงและสถานะต้นไม้พร็อพเพอร์ตี้ จะเป็นอินพุตสำหรับขั้นตอนการจัดเลเยอร์ของไปป์ไลน์การแสดงผล รายการข้อมูลทั้งหมดของชิ้นส่วนสีสามารถผสานเป็นเลเยอร์คอมโพสิตเลเยอร์เดียวและแรสเตอร์ร่วมกันได้ แต่วิธีนี้จะต้องทำการแรสเตอร์ที่สิ้นเปลืองทุกครั้งที่ผู้ใช้เลื่อน คุณอาจสร้างเลเยอร์คอมโพสิตสำหรับแต่ละกลุ่มการวาด แล้วแรสเตอร์ทีละกลุ่มเพื่อหลีกเลี่ยงการแรสเตอร์ใหม่ทั้งหมด แต่วิธีนี้จะทำให้หน่วยความจำ GPU หมดอย่างรวดเร็ว ขั้นตอนการจัดวางเลเยอร์ต้องหาจุดสมดุลระหว่างหน่วยความจํา GPU กับต้นทุนที่ลดลงเมื่อมีการเปลี่ยนแปลง แนวทางทั่วไปที่ดีคือการผสานข้อมูลโค้ดย่อยโดยค่าเริ่มต้น และจะไม่ผสานข้อมูลโค้ดย่อยของการวาดภาพที่มีสถานะต้นไม้พร็อพเพอร์ตี้ที่คาดว่าจะเปลี่ยนแปลงในเธรดคอมโพสิต เช่น กับการเลื่อนของเธรดคอมโพสิตหรือภาพเคลื่อนไหวการเปลี่ยนรูปแบบของเธรดคอมโพสิต
ตัวอย่างก่อนหน้านี้ควรสร้างเลเยอร์แบบคอมโพสิต 2 เลเยอร์ ดังนี้
- เลเยอร์คอมโพสิตขนาด 800x600 ที่มีคำสั่งวาด ดังนี้
drawRect
ขนาด 800x600 และสีขาวdrawRect
ขนาด 100x100 ที่ตําแหน่ง 0,0 และสีชมพู
- เลเยอร์คอมโพสิตขนาด 144x224 ที่มีคำสั่งวาด ดังนี้
drawTextBlob
ที่มีตําแหน่ง 0,0 และข้อความ "Hello world"- แปล 0,18
rotateZ(25deg)
drawRect
ขนาด 75x200 ที่ตําแหน่ง 0,0 และสีส้มdrawTextBlob
ที่มีตำแหน่ง 0,0 และข้อความ "ฉันกำลังตก"
หากผู้ใช้เลื่อน #scroll
ระบบจะย้ายเลเยอร์คอมโพสิตที่ 2 โดยไม่ต้องใช้การแรสเตอร์
สำหรับตัวอย่างจากส่วนก่อนหน้านี้เกี่ยวกับต้นไม้พร็อพเพอร์ตี้ จะมีชิ้นส่วนสี 6 ชิ้น สถานะเหล่านี้มีดังนี้
- พื้นหลังเอกสาร: การเลื่อนเอกสาร คลิปเอกสาร รูท การเลื่อนเอกสาร
- มุมแนวนอน แนวตั้ง และมุมเลื่อนสำหรับ div (กลุ่มการวาด 3 กลุ่มแยกกัน)
การเลื่อนเอกสาร คลิปเอกสาร
#one
เบลอ การเลื่อนเอกสาร - Iframe
#one
:#one
หมุน คลิปการเลื่อนแบบแสดงผลเกิน#one
เบลอ การเลื่อน div - Iframe
#two
:#two
scale, document clip, root, document scroll
เฟรมคอมโพสิต: พื้นผิว พื้นผิวการแสดงผล และไทล์พื้นผิว GPU
เบราว์เซอร์และกระบวนการแสดงผลจะจัดการการแรสเตอร์ของเนื้อหา จากนั้นส่งเฟรมคอมโพสิตไปยังกระบวนการแสดงผลภาพเพื่อแสดงบนหน้าจอ เฟรมคอมโพสิตแสดงวิธีต่อเนื้อหาที่แรสเตอร์เข้าด้วยกัน และวาดเนื้อหาอย่างมีประสิทธิภาพโดยใช้ GPU
การ์ด
ในทางทฤษฎีแล้ว คอมโพสิตกระบวนการแสดงผลหรือคอมโพสิตกระบวนการของเบราว์เซอร์อาจแรสเตอร์พิกเซลเป็นพื้นผิวเดียวขนาดเต็มของวิวพอร์ตโปรแกรมแสดงผล และส่งพื้นผิวนั้นไปยัง Viz หากต้องการแสดงผล คอมโพสิตการแสดงผลจะต้องคัดลอกพิกเซลจากพื้นผิวเดียวนั้นไปยังตําแหน่งที่เหมาะสมในเฟรมบัฟเฟอร์ (เช่น หน้าจอ) อย่างไรก็ตาม หากผู้ประกอบภาพต้องการอัปเดตแม้แต่พิกเซลเดียว ก็จะต้องแรสเตอร์วิดเจ็ตภาพเต็มอีกครั้งและส่งพื้นผิวใหม่ไปยัง Viz
แต่วิวพอร์ตจะแบ่งออกเป็นไทล์แทน แต่ละชิ้นส่วนของพื้นผิว GPU แยกกันรองรับแต่ละชิ้นส่วนด้วยพิกเซลที่แรสเตอร์สำหรับบางส่วนของวิวพอร์ต จากนั้นโปรแกรมแสดงผลจะอัปเดตแต่ละการ์ด หรือแม้แต่เปลี่ยนตำแหน่งบนหน้าจอของการ์ดที่มีอยู่ ตัวอย่างเช่น เมื่อเลื่อนเว็บไซต์ ตำแหน่งของไทล์ที่มีอยู่จะเลื่อนขึ้น และจะต้องแรสเตอร์ไทล์ใหม่เป็นครั้งคราวสำหรับเนื้อหาที่เลื่อนลงไปด้านล่างของหน้า
รูปสี่เหลี่ยมจัตุรัสและพื้นผิว
ไทล์พื้นผิวของ GPU เป็น Quad ชนิดพิเศษ ซึ่งเป็นชื่อที่เก๋ไก๋สำหรับพื้นผิวหมวดหมู่หนึ่งๆ รูปสี่เหลี่ยมระบุพื้นผิวอินพุต และระบุวิธีเปลี่ยนรูปแบบและใช้เอฟเฟกต์ภาพ ตัวอย่างเช่น ไทล์เนื้อหาปกติจะมีการเปลี่ยนรูปแบบที่ระบุตำแหน่ง x, y ในตารางกริดของไทล์
ไทล์ที่แรสเตอร์เหล่านี้จะรวมอยู่ในเรนเดอร์พาส ซึ่งเป็นรายการสี่เหลี่ยมจัตุรัส พาสการแสดงผลไม่มีข้อมูลพิกเซล แต่จะมีวิธีการวาดแต่ละรูปสี่เหลี่ยมจัตุรัสเพื่อผลิตเอาต์พุตพิกเซลที่ต้องการ มี draw quad สำหรับแต่ละไทล์พื้นผิว GPU คอมโพสิตการแสดงผลเพียงแค่ต้องวนดูรายการสี่เหลี่ยมจัตุรัส วาดแต่ละรายการด้วยเอฟเฟกต์ภาพที่ระบุ เพื่อผลิตเอาต์พุตพิกเซลที่ต้องการสำหรับพาสการแสดงผล การคอมโพสิทวาดรูปสี่เหลี่ยมจัตุรัสสําหรับการผ่านการแสดงผลทําได้อย่างมีประสิทธิภาพใน GPU เนื่องจากเอฟเฟกต์ภาพที่อนุญาตได้รับการเลือกอย่างรอบคอบเพื่อให้แมปกับฟีเจอร์ GPU โดยตรง
นอกเหนือจากสี่เหลี่ยมจัตุรัสแรสเตอร์แล้ว ยังมีสี่เหลี่ยมจัตุรัสวาดประเภทอื่นๆ เพิ่มเติม เช่น มีรูปสี่เหลี่ยมจัตุรัสสำหรับวาดสีพื้นที่ไม่มีพื้นผิวรองรับเลย หรือรูปสี่เหลี่ยมจัตุรัสสำหรับวาดพื้นผิวสำหรับพื้นผิวที่ไม่ใช่ไทล์ เช่น วิดีโอหรือภาพพิมพ์แคนวาส
นอกจากนี้ เฟรมคอมโพสิตยังฝังเฟรมคอมโพสิตอื่นได้ด้วย เช่น คอมโพสิตเบราว์เซอร์จะสร้างเฟรมคอมโพสิตที่มี UI ของเบราว์เซอร์ และสี่เหลี่ยมผืนผ้าว่างเปล่าที่จะฝังเนื้อหาคอมโพสิตการแสดงผล อีกตัวอย่างหนึ่งคือ iframe ที่แยกเว็บไซต์ การฝังนี้ทำได้ผ่านแพลตฟอร์ม
เมื่อผู้จัดองค์ประกอบส่งเฟรมผู้จัดองค์ประกอบ เฟรมดังกล่าวจะมีตัวระบุที่เรียกว่ารหัสแพลตฟอร์ม ซึ่งช่วยให้เฟรมผู้จัดองค์ประกอบอื่นๆ สามารถฝังเฟรมดังกล่าวได้โดยอ้างอิง Viz จะจัดเก็บเฟรมคอมโพสิตใหม่ล่าสุดที่ส่งมาพร้อมรหัสพื้นผิวหนึ่งๆ จากนั้นเฟรมคอมโพสิตอื่นจะอ้างอิงเฟรมดังกล่าวในภายหลังผ่านรูปสี่เหลี่ยมจัตุรัสสำหรับวาดพื้นผิวได้ ด้วยเหตุนี้ Viz จึงรู้ว่าต้องวาดอะไร (โปรดทราบว่าสี่เหลี่ยมจัตุรัสการวาดพื้นผิวจะมีเฉพาะรหัสพื้นผิวเท่านั้น โดยจะไม่มีพื้นผิว)
ผ่านการแสดงผลขั้นกลาง
เอฟเฟกต์ภาพบางรายการ เช่น ฟิลเตอร์จำนวนมากหรือโหมดการผสมขั้นสูง จำเป็นต้องวาดรูปสี่เหลี่ยมจัตุรัสอย่างน้อย 2 รูปลงในพื้นผิวระดับกลาง จากนั้นระบบจะวาดพื้นผิวระดับกลางลงในบัฟเฟอร์ปลายทางบน GPU (หรืออาจเป็นพื้นผิวระดับกลางอีกแบบหนึ่ง) โดยจะใช้เอฟเฟกต์ภาพไปพร้อมๆ กัน เฟรมคอมโพสิตจึงมีรายการของพาสการเรนเดอร์เพื่อให้รองรับการทำงานนี้ เสมอจะมีพาสการแสดงผลรูท ซึ่งจะวาดเป็นลำดับสุดท้ายและปลายทางสอดคล้องกับเฟรมบัฟเฟอร์ และอาจมีพาสอื่นๆ เพิ่มเติม
การที่อาจมีหลายผ่านการแสดงผลจึงเป็นที่มาของชื่อ "ผ่านการแสดงผล" แต่ละรอบต้องดำเนินการตามลำดับใน GPU ใน "รอบ" หลายรอบ ในขณะที่รอบเดียวจะดำเนินการเสร็จสมบูรณ์ได้ในการคำนวณแบบขนานจำนวนมากของ GPU ครั้งเดียว
การรวม
ระบบจะส่งเฟรมคอมโพสิตหลายเฟรมไปยัง Viz และจะต้องวาดเฟรมเหล่านั้นบนหน้าจอพร้อมกัน ซึ่งทำได้ด้วยระยะการรวมข้อมูลที่จะแปลงเฟรมคอมโพสิตต่างๆ ให้เป็นเฟรมคอมโพสิตแบบรวมเฟรมเดียว การรวมข้อมูลจะแทนที่สี่เหลี่ยมจัตุรัสการวาดพื้นผิวด้วยเฟรมคอมโพสิตที่ระบุ นอกจากนี้ยังเป็นโอกาสในการเพิ่มประสิทธิภาพพื้นผิวหรือเนื้อหาระดับกลางที่ไม่จำเป็นซึ่งอยู่นอกหน้าจอ เช่น ในหลายกรณี เฟรมคอมโพสิตสำหรับ iframe ที่แยกส่วนของเว็บไซต์ออกนั้นไม่จำเป็นต้องใช้เท็กเจอร์กลางของตัวเอง และสามารถวาดลงในเฟรมบัฟเฟอร์ได้โดยตรงผ่านสี่เหลี่ยมจัตุรัสการวาดที่เหมาะสม ระยะการรวบรวมข้อมูลจะหาวิธีเพิ่มประสิทธิภาพดังกล่าวและใช้ตามความรู้แบบองค์รวมที่คอมโพสิเตอร์เรนเดอร์แต่ละรายไม่สามารถเข้าถึงได้
ตัวอย่าง
ต่อไปนี้คือเฟรมคอมโพสิตที่แสดงตัวอย่างจากตอนต้นของโพสต์นี้
foo.com/index.html
surface: id=0- ผ่านการแสดงผล 0: วาดไปยังเอาต์พุต
- วาดรูปสี่เหลี่ยมจัตุรัสในการแสดงผล: วาดด้วยการเบลอ 3 พิกเซลและตัดคลิปไปยังการแสดงผล 0
- เรนเดอร์พาส 1:
- วาดสี่เหลี่ยมจัตุรัสสำหรับเนื้อหาการ์ดของ
#one
iframe พร้อมตำแหน่ง x และ y ของแต่ละรายการ
- วาดสี่เหลี่ยมจัตุรัสสำหรับเนื้อหาการ์ดของ
- เรนเดอร์พาส 1:
- รูปสี่เหลี่ยมจัตุรัสการวาดพื้นผิว: มีรหัส 2 วาดด้วยการเปลี่ยนรูปแบบการปรับขนาดและการเปลี่ยนตำแหน่ง
- วาดรูปสี่เหลี่ยมจัตุรัสในการแสดงผล: วาดด้วยการเบลอ 3 พิกเซลและตัดคลิปไปยังการแสดงผล 0
- ผ่านการแสดงผล 0: วาดไปยังเอาต์พุต
- แพลตฟอร์ม UI ของเบราว์เซอร์: ID=1
- ผ่านการแสดงผล 0: วาดไปยังเอาต์พุต
- วาดรูปสี่เหลี่ยมจัตุรัสสําหรับ UI เบราว์เซอร์ (ใช้การแบ่งส่วนด้วย)
- ผ่านการแสดงผล 0: วาดไปยังเอาต์พุต
bar.com/index.html
surface: ID=2- ผ่านการแสดงผล 0: วาดไปยังเอาต์พุต
- วาดสี่เหลี่ยมจัตุรัสสำหรับเนื้อหาของ
#two
iframe พร้อมตำแหน่ง x และ y ของแต่ละรายการ
- วาดสี่เหลี่ยมจัตุรัสสำหรับเนื้อหาของ
- ผ่านการแสดงผล 0: วาดไปยังเอาต์พุต
ภาพโดย Una Kravets