โพสต์ก่อนหน้าในชุดนี้อธิบายภาพรวมของเป้าหมาย พร็อพเพอร์ตี้หลัก และส่วนประกอบระดับสูงของสถาปัตยกรรม RenderingNG ต่อไปเรามาเจาะลึกโครงสร้างข้อมูลที่สำคัญซึ่งเป็นอินพุตและเอาต์พุตในไปป์ไลน์การแสดงผลกัน
โครงสร้างข้อมูลมีดังนี้
- แผนผังเฟรม ซึ่งประกอบด้วยโหนดในเครื่องและโหนดระยะไกลที่แสดงว่าเอกสารในเว็บใดอยู่ในกระบวนการแสดงผลและตัวแสดงผล Blink ใด
- แผนผังส่วนย่อยที่เปลี่ยนแปลงไม่ได้ แสดงเอาต์พุตของ (และอินพุตไปยัง) อัลกอริทึมข้อจำกัดเลย์เอาต์
- แผนผังพร็อพเพอร์ตี้ซึ่งแสดงถึงลำดับชั้นของการเปลี่ยนรูปแบบ คลิป เอฟเฟกต์ และการเลื่อนของเอกสารในเว็บ และใช้ตลอดไปป์ไลน์
- รายการแสดงรายการและกลุ่มสีเป็นอินพุตสำหรับอัลกอริทึมแรสเตอร์และเลเยอร์
- เฟรมประกอบจะห่อหุ้มพื้นผิว พื้นผิวแสดงผล และชิ้นส่วนพื้นผิว GPU ที่ใช้วาดโดยใช้ GPU
ก่อนที่จะอธิบายโครงสร้างข้อมูลเหล่านี้ ผมอยากแสดงตัวอย่างง่ายๆ ต่อไปนี้ซึ่งต่อยอดมาจากโพสต์ก่อนหน้า ฉันจะใช้ตัวอย่างนี้ 2-3 ครั้งในโพสต์นี้ เพื่อแสดงให้เห็นว่าโครงสร้างข้อมูลมีผลกับส่วนนี้อย่างไร
<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 ตัว แต่ปัจจุบันมี Fragment ของเฟรมในเครื่อง 3 ส่วน โดยมี 2 ส่วนในกระบวนการแสดงผลสำหรับ foo.com
และอีก 1 รายการในกระบวนการแสดงผลสำหรับ bar.com
ดังนี้
ในการสร้างเฟรมคอมโพสิเตอร์ 1 เฟรมสำหรับหน้าเว็บ Viz จะขอเฟรมคอมโพสิเตอร์จากเฟรมรูทของเฟรมทรีในพื้นที่ทั้ง 3 ตัวพร้อมกัน จากนั้นรวบรวม (โปรดดูส่วนเฟรมคอมโพสิเตอร์ในภายหลังในโพสต์นี้)
เฟรมหลักของ foo.com
และเฟรมย่อย foo.com/other-page
เป็นส่วนหนึ่งของแผนผังเฟรมเดียวกันและแสดงผลในกระบวนการเดียวกัน
อย่างไรก็ตาม ทั้ง 2 เฟรมยังคงมีวงจรเอกสารเป็นอิสระจากกันเนื่องจากเป็นส่วนหนึ่งของส่วนย่อยของแผนผังเฟรมในพื้นที่ที่ต่างกัน
ด้วยเหตุนี้ คุณจึงไม่สามารถสร้างเฟรมตัวประกอบ 1 เฟรมสำหรับทั้งสองการอัปเดตครั้งเดียวได้
กระบวนการแสดงผลมีข้อมูลไม่เพียงพอที่จะทำการผสมเฟรมคอมโพสิเตอร์ที่สร้างขึ้นสำหรับ foo.com/other-page
ลงในเฟรมคอมโพสิเตอร์สำหรับเฟรมหลักของ foo.com
โดยตรง
ตัวอย่างเช่น เฟรมระดับบนสุด bar.com
นอกกระบวนการอาจส่งผลต่อการแสดง foo.com/other-url
iframe โดยการเปลี่ยนรูปแบบ iframe ด้วย CSS หรือบดบังบางส่วนของ iframe ไว้กับองค์ประกอบอื่นๆ ใน DOM
Waterfall การอัปเดตพร็อพเพอร์ตี้ภาพ
คุณสมบัติด้านภาพ เช่น ปัจจัยที่มีผลต่อขนาดของอุปกรณ์และขนาดวิวพอร์ตจะส่งผลต่อเอาต์พุตที่แสดง และต้องซิงค์ระหว่างส่วนย่อยของแผนผังเฟรมในเครื่อง รูทของส่วนย่อยโครงสร้างเฟรมในเครื่องแต่ละรายการมีออบเจ็กต์วิดเจ็ตที่เชื่อมโยงอยู่ อัปเดตพร็อพเพอร์ตี้ภาพไปยังวิดเจ็ตของเฟรมหลักก่อนที่จะเผยแพร่ไปยังวิดเจ็ตที่เหลือจากบนลงล่าง ตัวอย่างเช่น เมื่อขนาดวิวพอร์ตเปลี่ยนแปลง
กระบวนการนี้จะไม่เกิดขึ้นทันที ดังนั้นพร็อพเพอร์ตี้ภาพที่มีการจำลองจึงมีโทเค็นการซิงค์ด้วย คอมโพสิเตอร์ของ Viz ใช้โทเค็นการซิงค์นี้เพื่อรอให้ Fragment เฟรมแผนผังในเครื่องทั้งหมดส่งเฟรมคอมโพสิเตอร์กับโทเค็นการซิงค์ปัจจุบัน กระบวนการนี้หลีกเลี่ยงการผสมเฟรมคอมโพสิเตอร์กับคุณสมบัติภาพที่แตกต่างกัน
แผนผัง Fragment ที่เปลี่ยนแปลงไม่ได้
แผนผัง Fragment ที่เปลี่ยนแปลงไม่ได้คือเอาต์พุตของขั้นตอนเลย์เอาต์ของไปป์ไลน์การแสดงผล โดยแสดงตำแหน่งและขนาดขององค์ประกอบทั้งหมดในหน้าเว็บ (โดยไม่ใช้การแปลง)
ส่วนย่อยแต่ละส่วนแสดงส่วนหนึ่งขององค์ประกอบ DOM โดยปกติแล้วจะมีส่วนย่อยเพียงส่วนเดียวต่อองค์ประกอบเท่านั้น แต่มีมากกว่านี้ได้หากมีการแยกส่วนตามหน้าต่างๆ เมื่อพิมพ์ หรือมีหลายคอลัมน์เมื่ออยู่ในบริบทหลายคอลัมน์
หลังจากเลย์เอาต์ ส่วนย่อยแต่ละส่วนจะเปลี่ยนแปลงไม่ได้และไม่มีการเปลี่ยนแปลงอีก ที่สำคัญ เรายังมีข้อจำกัดเพิ่มเติมอีก 2-3 ข้ออีกด้วย เราจะไม่ดำเนินการต่อไปนี้
- อนุญาตการอ้างอิง "ขึ้น" ในโครงสร้าง (เด็กไม่สามารถมีตัวชี้ไปยังระดับบนสุดได้)
- ข้อมูล "ฟองสบู่" ตามต้นไม้ (เด็กอ่านข้อมูลจากบุตรหลานเท่านั้น ไม่ได้อ่านข้อมูลจากพ่อแม่)
ข้อจำกัดเหล่านี้ช่วยให้เราสามารถนำส่วนย่อยมาใช้ซ้ำสำหรับเลย์เอาต์ที่ตามมาได้ หากไม่มีข้อจำกัดเหล่านี้ เราคงต้องสร้างต้นใหม่ทั้งต้นซึ่งมีราคาแพงอยู่บ่อยครั้ง
เลย์เอาต์ส่วนใหญ่มักเป็นการอัปเดตแบบค่อยเป็นค่อยไป เช่น เว็บแอปที่อัปเดต UI ส่วนเล็กๆ เพื่อตอบสนองต่อการที่ผู้ใช้คลิกองค์ประกอบ ตามหลักการแล้ว เลย์เอาต์ควรจะทำงานตามสัดส่วนของสิ่งที่เปลี่ยนแปลงบนหน้าจอจริงๆ เท่านั้น เราสามารถทำได้โดยการนำส่วนต่างๆ ของโครงสร้างก่อนหน้านี้มาใช้ซ้ำให้ได้มากที่สุด ซึ่งหมายความว่า (โดยปกติ) เราจะต้องสร้างเฉพาะกระดูกสันหลังของต้นไม้เท่านั้น
ในอนาคต การออกแบบที่เปลี่ยนแปลงไม่ได้นี้จะช่วยให้เราทําสิ่งต่างๆ ที่น่าสนใจได้ เช่น การส่งแผนผังส่วนย่อยที่เปลี่ยนแปลงไม่ได้ผ่านขอบเขตของเทรด หากจำเป็น (เพื่อดําเนินการระยะต่อๆ ไปในชุดข้อความอื่น) สร้างต้นไม้หลายต้นสำหรับภาพเคลื่อนไหวของเลย์เอาต์ที่ราบรื่น หรือทำเลย์เอาต์ที่คาดเดาขนานกัน นอกจากนี้ยังทำให้เราได้ใช้ประโยชน์จากเลย์เอาต์แบบชุดข้อความแบบหลายรายการด้วย
รายการส่วนย่อยในบรรทัด
เนื้อหาในบรรทัด (ข้อความที่จัดรูปแบบเป็นส่วนใหญ่) ใช้การนำเสนอที่ต่างออกไปเล็กน้อย แทนที่จะเป็นโครงสร้างแบบต้นไม้ที่มีช่องและตัวชี้ เราแสดงเนื้อหาแบบแทรกในบรรทัดเป็นรายการแบบเดี่ยวที่แทนต้นไม้ ประโยชน์หลักคือการแสดงรายชื่อแบบแฟลตสำหรับอินไลน์ที่รวดเร็ว มีประโยชน์สำหรับการตรวจสอบหรือค้นหาโครงสร้างข้อมูลแบบอินไลน์ ตลอดจนหน่วยความจำที่มีประสิทธิภาพ ซึ่งเป็นสิ่งสำคัญมากต่อประสิทธิภาพการแสดงผลเว็บเนื่องจากการแสดงผลข้อความมีความซับซ้อนมาก และอาจกลายเป็นส่วนที่ช้าที่สุดของไปป์ไลน์ได้ง่าย เว้นแต่จะมีการเพิ่มประสิทธิภาพสูงสุด
หมายเหตุทางประวัติศาสตร์ที่น่าสนใจนี้คล้ายกับ วิธีที่ Internet Explorer เคยแสดง DOM ไว้ เนื่องจากตอนแรกถูกสร้างขึ้นในลักษณะที่คล้ายกันกับเครื่องมือแก้ไขข้อความ
ระบบจะสร้างรายการแบบแฟลตลิสต์สำหรับบริบทการจัดรูปแบบอินไลน์แต่ละรายการตามลำดับของการค้นหาแบบเจาะลึกของแผนผังย่อยของเลย์เอาต์แบบอินไลน์ แต่ละรายการในลิสต์เป็นทูเปิลของ (ออบเจ็กต์ จำนวนองค์ประกอบสืบทอด) ลองดูตัวอย่าง DOM นี้
<div style="width: 0;">
<span style="color: blue; position: relative;">Hi</span> <b>there</b>.
</div>
(โปรดทราบว่าพร็อพเพอร์ตี้ width
มีค่าเป็น 0 เพื่อให้เส้นตัดระหว่าง "Hi" และ "มี")
เมื่อบริบทการจัดรูปแบบอินไลน์สำหรับสถานการณ์นี้แสดงแบบต้นไม้ จะมีลักษณะดังต่อไปนี้
{
"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 สามารถใช้รูปแบบต่างๆ กับองค์ประกอบ
โดยส่วนใหญ่จะมีเอฟเฟกต์ 4 รสชาติดังนี้
- เลย์เอาต์: อินพุตไปยังอัลกอริทึมข้อจํากัดเลย์เอาต์
- สี: วิธีระบายสีและแรสเตอร์องค์ประกอบ (แต่ไม่ใช่องค์ประกอบสืบทอด)
- ภาพ: เอฟเฟกต์แรสเตอร์/การวาดที่ใช้กับแผนผังย่อย DOM เช่น การเปลี่ยนรูปแบบ ฟิลเตอร์ และการคลิป
- การเลื่อน: การตัดและเลื่อนมุมโค้งตามแกน ของแผนผังย่อยที่มีอยู่
แผนผังพร็อพเพอร์ตี้เป็นโครงสร้างข้อมูลที่อธิบายว่าเอฟเฟกต์ภาพและการเลื่อนมีผลกับองค์ประกอบ DOM อย่างไร เครื่องมือนี้จะให้วิธีการในการตอบคำถามต่างๆ เช่น องค์ประกอบ DOM หนึ่งๆ ที่ให้โดยสัมพันธ์กับหน้าจออย่างไร เมื่อพิจารณาจากขนาดและตำแหน่งของเลย์เอาต์ และควรใช้ลำดับการดำเนินการ GPU ในข้อใดเพื่อนำเอฟเฟกต์ภาพและการเลื่อนไปใช้
เอฟเฟกต์ภาพและการเลื่อนบนเว็บมีความซับซ้อนอย่างยิ่ง ดังนั้นสิ่งที่สำคัญที่สุดที่โครงสร้างต้นไม้ของพร็อพเพอร์ตี้ทำคือการแปลงความซับซ้อนเป็นโครงสร้างข้อมูลเดียวที่แสดงถึงโครงสร้างและความหมายได้อย่างแม่นยำ ในขณะเดียวกันก็กำจัดความซับซ้อนที่เหลือของ DOM และ CSS ไปพร้อมๆ กัน วิธีนี้ช่วยให้เราใช้อัลกอริทึมในการเรียบเรียงและการเลื่อนได้อย่างมั่นใจมากขึ้น โดยเฉพาะอย่างยิ่งฟีเจอร์ต่อไปนี้
- เรขาคณิตและการคำนวณอื่นๆ ที่อาจมีข้อผิดพลาดอาจรวมอยู่ในที่เดียว
- ความซับซ้อนในการสร้างและอัปเดตต้นไม้พร็อพเพอร์ตี้จะแยกเป็นขั้นตอนเดียวในไปป์ไลน์การแสดงผล
- การส่งต้นไม้พร็อพเพอร์ตี้ไปยังเทรดและการประมวลผลต่างๆ นั้นง่ายดายและรวดเร็วกว่าสถานะ DOM แบบเต็ม ทำให้สามารถใช้องค์ประกอบเหล่านี้สำหรับกรณีการใช้งานต่างๆ ได้
- ยิ่งมี Use Case มาก เราจะได้ประโยชน์มากขึ้นจากการแคชเรขาคณิตที่สร้างมาอย่างดี เนื่องจากสามารถนำแคชของกันและกันกลับมาใช้ใหม่ได้
RenderingNG ใช้แผนผังพร็อพเพอร์ตี้เพื่อวัตถุประสงค์หลายประการ เช่น
- การแยกการประกอบออกจากสี และการประกอบจากเทรดหลัก
- การกำหนดกลยุทธ์การประกอบ / วาดที่มีประสิทธิภาพสูงสุด
- การวัดเรขาคณิต IntersectionObserver
- หลีกเลี่ยงการทำงานกับองค์ประกอบนอกหน้าจอและชิ้นส่วนพื้นผิว GPU
- ทำให้สีและแรสเตอร์ใช้ไม่ได้อย่างมีประสิทธิภาพและแม่นยำ
- การวัดการเปลี่ยนแปลงของเลย์เอาต์และ Largest Contentful Paint ใน Core Web Vitals
เอกสารในเว็บแต่ละฉบับมีแผนผังพร็อพเพอร์ตี้แยกกัน 4 ต้นไม้ ได้แก่ transform, Clip, เอฟเฟกต์ และการเลื่อน (*) ต้นไม้ Transform จะแสดงการแปลงและการเลื่อนของ CSS (การเปลี่ยนรูปแบบการเลื่อนจะแสดงเป็นเมทริกซ์การแปลงแบบ 2 มิติ) แผนผังคลิปแสดงคลิปเพิ่มเติม แผนผังเอฟเฟกต์จะแสดงเอฟเฟกต์ภาพอื่นๆ ทั้งหมด ได้แก่ ความทึบแสง ฟิลเตอร์ มาสก์ โหมดผสมผสาน และคลิปประเภทอื่นๆ เช่น เส้นทางคลิป แผนผังการเลื่อนแสดงข้อมูลเกี่ยวกับการเลื่อน เช่น วิธีที่การเลื่อนห่วงโซ่เข้าด้วยกัน จำเป็นต่อการเลื่อนชุดข้อความของตัวจัดวาง แต่ละโหนดในแผนผังพร็อพเพอร์ตี้แสดงถึงการเลื่อนหรือเอฟเฟกต์แบบภาพที่องค์ประกอบ DOM ใช้ หากเกิดผลกระทบหลายอย่าง อาจมีโหนดทรีของพร็อพเพอร์ตี้มากกว่า 1 โหนดในแต่ละต้นไม้สำหรับองค์ประกอบเดียวกัน
โทโพโลยีของแต่ละต้นไม้เหมือนกับการแสดง DOM เพียงเล็กน้อย ตัวอย่างเช่น หากมีองค์ประกอบ DOM 3 รายการที่มีคลิปรายการเพิ่มเติม ก็จะมีโหนดต้นไม้คลิป 3 โหนด และโครงสร้างของแผนผังคลิปจะเป็นไปตามความสัมพันธ์แบบบล็อกที่มีระหว่างคลิปรายการเพิ่มเติม และยังมีการเชื่อมโยงระหว่างต้นไม้เหล่านี้ด้วย ลิงก์เหล่านี้จะระบุลำดับชั้น 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 เพื่อสร้างลิสต์รายการที่แสดง
เช่น
<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 ที่เป็นค่าลบก่อน (ขั้นตอนที่ 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 และส่งข้อความ "สวัสดีโลก" |
ชุดรายการที่แสดงจะได้รับการจัดเก็บและนำมาใช้ซ้ำเมื่อมีการอัปเดตในภายหลัง หากออบเจ็กต์เลย์เอาต์ไม่มีการเปลี่ยนแปลงระหว่างการเดินแบบ Paint Tree ระบบจะคัดลอกรายการที่แสดงมาจากรายการก่อนหน้า การเพิ่มประสิทธิภาพเพิ่มเติมอาศัยพร็อพเพอร์ตี้ของข้อกำหนดลำดับสี 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 และส่งข้อความ "สวัสดีโลก" |
drawRect ที่มีขนาด 75x200 ที่ตำแหน่ง 0,0 และสีส้ม |
drawTextBlob ที่มีตำแหน่ง 0,0 และส่งข้อความ "ฉันกำลังตก" |
แผนผังพร็อพเพอร์ตี้และชิ้นส่วนสีในการเปลี่ยนรูปแบบควรมีลักษณะต่อไปนี้ (พูดง่ายๆ เพื่อความสั้นกระชับ)
รายการตามลำดับของชิ้นส่วนสี ซึ่งเป็นกลุ่มของรายการที่แสดงและสถานะของโครงสร้างพร็อพเพอร์ตี้ คืออินพุตสำหรับขั้นตอนการสร้างเลเยอร์ของไปป์ไลน์การแสดงผล เราสามารถรวมรายการชิ้นส่วนสีทั้งหมดให้เป็นเลเยอร์แบบผสมเดี่ยวและแรสเตอร์เข้าด้วยกัน แต่การแรสเตอร์ที่มีค่าใช้จ่ายสูงในแต่ละครั้งที่ผู้ใช้เลื่อน คุณอาจสร้างเลเยอร์ที่ทำการผสมสำหรับชิ้นส่วนสีแต่ละชิ้นและทำการแรสเตอร์แยกกันเพื่อหลีกเลี่ยงการแรสเตอร์ซ้ำทั้งหมด แต่จะทำให้ใช้หน่วยความจำ GPU หมดอย่างรวดเร็ว โดยขั้นตอนการกำหนดเลเยอร์จะต้องเกิดความคุ้มค่าระหว่างหน่วยความจำ GPU กับการลดค่าใช้จ่ายเมื่อมีการเปลี่ยนแปลง วิธีการทั่วไปที่ดีคือการผสานชิ้นส่วนโดยค่าเริ่มต้น และไม่ผสานชิ้นส่วนสีที่มีสถานะแผนผังพร็อพเพอร์ตี้ที่คาดว่าจะเปลี่ยนแปลงในเทรดของคอมโพสิเตอร์ เช่น เมื่อมีภาพเคลื่อนไหวการเปลี่ยนรูปแบบเทรดคอมโพสิเตอร์ หรือเทรดคอมโพสิเตอร์
ตัวอย่างก่อนหน้านี้ควรสร้างเลเยอร์แบบผสม 2 เลเยอร์:
- เลเยอร์แบบผสมขนาด 800x600 ที่มีคำสั่งการวาดดังนี้
drawRect
ที่มีขนาด 800x600 และสีขาวdrawRect
ที่มีขนาด 100x100 ที่ตำแหน่ง 0,0 และมีสีชมพู
- เลเยอร์แบบผสมขนาด 144x224 ที่มีคำสั่งการวาดดังนี้
drawTextBlob
ที่มีตำแหน่ง 0,0 และส่งข้อความ "สวัสดีโลก"- แปลภาษา 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
, คลิปเอกสาร, รูท, การเลื่อนเอกสาร
เฟรมประกอบ: พื้นผิว พื้นผิวแสดงผล และชิ้นส่วนพื้นผิว GPU
ตามที่ได้กล่าวไว้ในโพสต์ก่อนหน้า (ดูตัวอย่างที่ใช้งานได้ที่นี่) กระบวนการของเบราว์เซอร์และแสดงผลจะจัดการการแรสเตอร์เนื้อหา จากนั้นส่งเฟรมตัวประกอบไปยังกระบวนการ Viz เพื่อนำเสนอไปยังหน้าจอ เฟรมผสมคือวิธีที่ RenderingNG แสดงวิธีรวมเนื้อหาที่แรสเตอร์เข้าด้วยกัน และวาดเนื้อหาอย่างมีประสิทธิภาพโดยใช้ GPU
การ์ด
ในทางทฤษฎี กระบวนการแสดงผลหรือตัวประมวลผลกระบวนการของเบราว์เซอร์สามารถแรสเตอร์พิกเซลเป็นพื้นผิวเดียวในขนาดเต็มของวิวพอร์ตการแสดงผลและส่งพื้นผิวนั้นไปยัง Viz เพื่อแสดงผล ตัวประมวลผลการแสดงผลจะต้องคัดลอกพิกเซลจากพื้นผิวเดียวนั้นไปยังตำแหน่งที่เหมาะสมในบัฟเฟอร์เฟรม (ตัวอย่างเช่น หน้าจอ) อย่างไรก็ตาม หากตัวประกอบนั้นต้องการอัปเดตแม้แต่พิกเซลเดียว ก็จะต้องแรสเตอร์วิวพอร์ตแบบเต็มอีกครั้งและส่งพื้นผิวใหม่ไปยัง Viz
แต่วิวพอร์ตจะถูกแบ่งออกเป็นชิ้นส่วน ชิ้นส่วนพื้นผิว GPU ที่แยกต่างหากจะอยู่หลังแต่ละชิ้นส่วนที่มีพิกเซลแรสเตอร์สำหรับส่วนหนึ่งของวิวพอร์ต จากนั้นโหมดแสดงภาพสามารถอัปเดตการ์ดแต่ละใบหรือแม้กระทั่งเปลี่ยนตำแหน่งบนหน้าจอของการ์ดที่มีอยู่ก็ได้ ตัวอย่างเช่น เมื่อเลื่อนเว็บไซต์ ตำแหน่งของชิ้นส่วนที่มีอยู่จะเลื่อนขึ้น และในบางครั้งก็จำเป็นต้องแรสเตอร์การ์ดใหม่สำหรับเนื้อหาที่อยู่ด้านล่างของหน้า
รูปภาพด้านบนแสดงภาพวันที่มีแดดจัด ซึ่งประกอบด้วย 4 ไทล์ เมื่อมีการเลื่อน การ์ดที่ 5 จะเริ่มปรากฏขึ้น การ์ดใบหนึ่งมีเพียงสีเดียว (สีฟ้า) และมีวิดีโอและ iframe อยู่ด้านบน ซึ่งจะนำไปสู่หัวข้อถัดไป
ควอดและพื้นผิว
ชิ้นส่วนพื้นผิว GPU เป็น quad ชนิดพิเศษ ซึ่งเป็นชื่อที่ใช้ง่ายสำหรับพื้นผิวหมวดหมู่ใดหมวดหมู่หนึ่ง สี่เหลี่ยมจัตุรัสจะระบุพื้นผิวอินพุต รวมถึงระบุวิธีเปลี่ยนรูปแบบและใช้เอฟเฟกต์ภาพกับภาพดังกล่าว ตัวอย่างเช่น ชิ้นส่วนเนื้อหาปกติจะมีการเปลี่ยนรูปแบบที่ระบุตำแหน่ง x และ y ในตารางกริดของไทล์
การ์ดแรสเตอร์เหล่านี้จะรวมอยู่ในผ่านการแสดงผล ซึ่งเป็นรายการสี่เหลี่ยมจัตุรัส การ์ดแสดงผลไม่มีข้อมูลพิกเซล แต่มีคำแนะนำเกี่ยวกับตำแหน่งและวิธีวาดสี่เหลี่ยมจัตุรัสแต่ละรูปเพื่อสร้างเอาต์พุตพิกเซลที่ต้องการ มีสี่เหลี่ยมจัตุรัสในการวาดสำหรับชิ้นส่วนพื้นผิว GPU แต่ละชิ้น ตัวประกอบการแสดงผลจะต้องทำซ้ำผ่านรายการสี่เหลี่ยมจัตุรัส วาดแต่ละภาพด้วยเอฟเฟกต์ภาพที่ระบุไว้ เพื่อสร้างเอาต์พุตพิกเซลที่ต้องการสำหรับการส่งแสดงผล การจัดองค์ประกอบสี่เหลี่ยมจัตุรัสในการแสดงผลสำหรับการส่งแสดงผลจะทำงานใน GPU ได้อย่างมีประสิทธิภาพ เนื่องจากเอฟเฟกต์ภาพที่ได้รับอนุญาตได้รับการเลือกมาพิถีพิถันให้เป็นเอฟเฟกต์ที่แมปกับฟีเจอร์ของ GPU โดยตรง
ยังมีสี่เหลี่ยมจัตุรัสในการวาดประเภทอื่นๆ นอกเหนือจากชิ้นส่วนแบบแรสเตอร์ เช่น มีสี่เหลี่ยมจัตุรัสสำหรับวาดสีทึบที่ไม่มีพื้นผิวเลย หรือสี่เหลี่ยมจัตุรัสสำหรับวาดพื้นผิวสำหรับพื้นผิวที่ไม่ใช่ชิ้นส่วน เช่น วิดีโอหรือแคนวาส
เฟรมคอมโพสิเตอร์อาจฝังเฟรมคอมโพสิเตอร์อื่นได้ด้วย ตัวอย่างเช่น โปรแกรมคอมโพสิเตอร์ของเบราว์เซอร์จะสร้างเฟรมคอมโพสิเตอร์ที่มี UI ของเบราว์เซอร์ และสี่เหลี่ยมผืนผ้าที่ว่างเปล่าซึ่งจะฝังเนื้อหาเครื่องมือแสดงผลในการแสดงผล อีกตัวอย่างหนึ่งคือ iframe ที่แยกส่วนเว็บไซต์ การฝังนี้ดำเนินการได้ผ่านแพลตฟอร์ม
เมื่อตัวคอมโพสิเตอร์ส่งเฟรมตัวประกอบ จะมีตัวระบุที่เรียกว่ารหัสพื้นผิวร่วมด้วย ซึ่งช่วยให้เฟรมคอมโพสิเตอร์อื่นๆ ฝังโค้ดดังกล่าวได้โดยการอ้างอิง Viz จะจัดเก็บข้อมูลเฟรม Compositor ใหม่ล่าสุดที่ส่งพร้อมรหัสพื้นผิวที่เฉพาะเจาะจง เฟรมนี้อ้างอิงองค์ประกอบอื่นจะอ้างอิงเฟรมดังกล่าวในภายหลังผ่านสี่เหลี่ยมจัตุรัสสำหรับวาดแพลตฟอร์มได้ วิซจึงรู้ว่าจะวาดอะไร (โปรดทราบว่าสี่เหลี่ยมจัตุรัสที่วาดพื้นผิวจะมีเฉพาะรหัสพื้นผิวเท่านั้น และไม่มีพื้นผิว)
การส่งผ่านการแสดงผลระดับกลาง
เอฟเฟ็กต์ภาพบางอย่าง เช่น ฟิลเตอร์ต่างๆ หรือโหมดผสานขั้นสูง กำหนดให้ต้องวาดสี่เหลี่ยมจัตุรัสสองชิ้นขึ้นไปบนพื้นผิวระดับกลาง จากนั้นจึงวาดพื้นผิวระดับกลางลงในบัฟเฟอร์ปลายทางของ GPU (หรืออาจเป็นพื้นผิวระดับกลางอีกแบบ) โดยใช้เอฟเฟกต์ภาพไปพร้อมๆ กัน ซึ่งจริงๆ แล้วเฟรมคอมโพสิเตอร์มีรายการของการส่งผ่านการแสดงผล โดยจะมีเส้นทางการแสดงผลรูทเสมอ ซึ่งจะถูกวาดเป็นลำดับสุดท้ายและปลายทางที่สอดคล้องกับเฟรมบัฟเฟอร์ และอาจมีเพิ่มเติม
ความเป็นไปได้ที่จะมีการส่งผ่านการแสดงผลหลายชุด จะอธิบายชื่อของ "ผ่านการแสดงผล" การเรียกแต่ละรายการจะต้องดำเนินการตามลำดับบน GPU เป็น "การส่งผ่าน" หลายครั้ง ขณะที่การส่ง 1 ครั้งจะเสร็จสิ้นได้ในการคำนวณ GPU แบบพร้อมกันจำนวนมาก
การรวม
มีการส่งเฟรมคอมโพสิเตอร์หลายเฟรมไปยัง Viz และต้องวาดลงในหน้าจอพร้อมกัน ซึ่งทำได้โดยขั้นตอนการรวมที่แปลงเป็นเฟรมคอมโพสิเตอร์แบบรวมเดี่ยว การรวมจะแทนที่สี่เหลี่ยมจัตุรัสที่วาดพื้นผิวด้วยเฟรมคอมโพสิเตอร์ที่ระบุ นอกจากนี้ยังเป็นโอกาสในการเพิ่มประสิทธิภาพไม่ให้พื้นผิวช่วงกลางที่ไม่จำเป็นหรือเนื้อหาที่อยู่ไกลออกไปนอกหน้าจอ ตัวอย่างเช่น ในหลายๆ กรณี เฟรมตัวประกอบสำหรับ iframe ที่แยกส่วนของเว็บไซต์ไม่จำเป็นต้องมีพื้นผิวตรงกลางของตัวเอง และคุณสามารถวาดลงในบัฟเฟอร์เฟรมได้โดยตรงผ่านสี่เหลี่ยมจัตุรัสที่เหมาะสม ขั้นตอนการรวมจะหาการเพิ่มประสิทธิภาพดังกล่าว และนำไปปรับใช้ตามความรู้ทั่วโลกที่ตัวประมวลผลการแสดงผลแต่ละแบบไม่สามารถเข้าถึงได้
ตัวอย่าง
นี่คือเฟรมตัวประกอบจริงที่เป็นตัวแทนตัวอย่างจากตอนต้นของโพสต์นี้
foo.com/index.html
แพลตฟอร์ม: 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
แพลตฟอร์ม: ID=2- แสดงผลผ่าน 0: วาดเพื่อให้แสดงผล
- วาดสี่เหลี่ยมสำหรับเนื้อหาของ iframe
#two
โดยมีตำแหน่ง x และ y สำหรับแต่ละรายการ
- วาดสี่เหลี่ยมสำหรับเนื้อหาของ iframe
- แสดงผลผ่าน 0: วาดเพื่อให้แสดงผล
บทสรุป
ขอขอบคุณที่อ่าน และเมื่อรวมกับ 2 โพสต์ก่อนหน้า ก็สรุปภาพรวมของ RenderingNG ได้ ถัดไปจะเป็นเจาะลึกเกี่ยวกับความท้าทายและเทคโนโลยีภายในองค์ประกอบย่อยหลายๆ องค์ประกอบของไปป์ไลน์การแสดงภาพ ตั้งแต่ต้นจนจบ สิ่งเหล่านี้จะพร้อมให้บริการในเร็วๆ นี้
ภาพโดย Una Kravets