โครงสร้างข้อมูลและบทบาทที่สำคัญใน 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

ก่อนที่จะอธิบายโครงสร้างข้อมูลเหล่านี้ ผมอยากแสดงตัวอย่างง่ายๆ ต่อไปนี้ซึ่งต่อยอดมาจากโพสต์ก่อนหน้า ฉันจะใช้ตัวอย่างนี้ 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 เฟรม ได้แก่

foo.com เฟรมระดับบนสุดที่มี iframe 2 รายการ

เมื่อใช้การแยกเว็บไซต์ Chromium จะใช้กระบวนการแสดงผล 2 แบบเพื่อแสดงผลหน้าเว็บนี้ กระบวนการแสดงผลแต่ละกระบวนการจะมีการแสดงแผนผังเฟรมของหน้าเว็บของตนเอง ดังนี้

แผนผัง 2 เฟรมที่แทนกระบวนการแสดงผล 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 ดังนี้

การแสดงภาพ 2 ภาพและส่วนย่อยของแผนผังเฟรม 3 เฟรม

ในการสร้างเฟรมคอมโพสิเตอร์ 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 ที่เปลี่ยนแปลงไม่ได้คือเอาต์พุตของขั้นตอนเลย์เอาต์ของไปป์ไลน์การแสดงผล โดยแสดงตำแหน่งและขนาดขององค์ประกอบทั้งหมดในหน้าเว็บ (โดยไม่ใช้การแปลง)

การแสดงส่วนย่อยในแต่ละต้นไม้ โดยส่วน 1 ส่วนจะถูกทำเครื่องหมายว่าจำเป็นต่อการออกแบบ

ส่วนย่อยแต่ละส่วนแสดงส่วนหนึ่งขององค์ประกอบ 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 เพื่อสร้างลิสต์รายการที่แสดง

เช่น

กล่องสีน้ำเงินที่มีคำว่า &quot;สวัสดีโลก&quot; ภายในสี่เหลี่ยมผืนผ้าสีเขียว

<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 เช่น การสอดแนมที่สร้างขึ้นจากส่วนต่างกำไรติดลบ ดังนี้

สี่เหลี่ยมผืนผ้าสีเขียวที่มีกล่องสีเทาวางซ้อนอยู่บางส่วนและคำว่า &quot;สวัสดีโลก&quot;

<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 และส่งข้อความ "ฉันกำลังตก"

แผนผังพร็อพเพอร์ตี้และชิ้นส่วนสีในการเปลี่ยนรูปแบบควรมีลักษณะต่อไปนี้ (พูดง่ายๆ เพื่อความสั้นกระชับ)

รูปภาพของตารางก่อนหน้า 2 เซลล์แรกในส่วนที่ 1 เซลล์ที่ 3 ในกลุ่มที่ 2 อีก 2 เซลล์สุดท้ายในส่วนที่ 3

รายการตามลำดับของชิ้นส่วนสี ซึ่งเป็นกลุ่มของรายการที่แสดงและสถานะของโครงสร้างพร็อพเพอร์ตี้ คืออินพุตสำหรับขั้นตอนการสร้างเลเยอร์ของไปป์ไลน์การแสดงผล เราสามารถรวมรายการชิ้นส่วนสีทั้งหมดให้เป็นเลเยอร์แบบผสมเดี่ยวและแรสเตอร์เข้าด้วยกัน แต่การแรสเตอร์ที่มีค่าใช้จ่ายสูงในแต่ละครั้งที่ผู้ใช้เลื่อน คุณอาจสร้างเลเยอร์ที่ทำการผสมสำหรับชิ้นส่วนสีแต่ละชิ้นและทำการแรสเตอร์แยกกันเพื่อหลีกเลี่ยงการแรสเตอร์ซ้ำทั้งหมด แต่จะทำให้ใช้หน่วยความจำ GPU หมดอย่างรวดเร็ว โดยขั้นตอนการกำหนดเลเยอร์จะต้องเกิดความคุ้มค่าระหว่างหน่วยความจำ GPU กับการลดค่าใช้จ่ายเมื่อมีการเปลี่ยนแปลง วิธีการทั่วไปที่ดีคือการผสานชิ้นส่วนโดยค่าเริ่มต้น และไม่ผสานชิ้นส่วนสีที่มีสถานะแผนผังพร็อพเพอร์ตี้ที่คาดว่าจะเปลี่ยนแปลงในเทรดของคอมโพสิเตอร์ เช่น เมื่อมีภาพเคลื่อนไหวการเปลี่ยนรูปแบบเทรดคอมโพสิเตอร์ หรือเทรดคอมโพสิเตอร์

ตัวอย่างก่อนหน้านี้ควรสร้างเลเยอร์แบบผสม 2 เลเยอร์:

  • เลเยอร์แบบผสมขนาด 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 ระบบจะย้ายเลเยอร์ที่ 2 ที่ได้แบบผสมไว้ แต่ไม่จำเป็นต้องแรสเตอร์

ดูตัวอย่างได้ที่นี่ จากส่วนก่อนหน้าเกี่ยวกับแผนผังพร็อพเพอร์ตี้ จะมีชิ้นส่วนสีอยู่ 6 กลุ่ม เมื่อใช้ร่วมกับสถานะโครงสร้างต้นไม้ของพร็อพเพอร์ตี้ (เปลี่ยนรูปแบบ คลิป เอฟเฟกต์ เลื่อน) ซึ่งได้แก่

  • พื้นหลังเอกสาร: การเลื่อนเอกสาร คลิปเอกสาร รูท การเลื่อนเอกสาร
  • มุมแนวนอน แนวตั้ง และมุมการเลื่อนสำหรับ div (ส่วนสีแบ่ง 3 ส่วน) ได้แก่ การเลื่อนเอกสาร คลิปเอกสาร การเบลอ #one การเลื่อนเอกสาร
  • iframe #one: หมุน #one, คลิปเลื่อนส่วนเกิน, เบลอ #one, การเลื่อน div
  • iframe #two: มาตราส่วน #two, คลิปเอกสาร, รูท, การเลื่อนเอกสาร

เฟรมประกอบ: พื้นผิว พื้นผิวแสดงผล และชิ้นส่วนพื้นผิว GPU

ตามที่ได้กล่าวไว้ในโพสต์ก่อนหน้า (ดูตัวอย่างที่ใช้งานได้ที่นี่) กระบวนการของเบราว์เซอร์และแสดงผลจะจัดการการแรสเตอร์เนื้อหา จากนั้นส่งเฟรมตัวประกอบไปยังกระบวนการ Viz เพื่อนำเสนอไปยังหน้าจอ เฟรมผสมคือวิธีที่ RenderingNG แสดงวิธีรวมเนื้อหาที่แรสเตอร์เข้าด้วยกัน และวาดเนื้อหาอย่างมีประสิทธิภาพโดยใช้ GPU

การ์ด

ในทางทฤษฎี กระบวนการแสดงผลหรือตัวประมวลผลกระบวนการของเบราว์เซอร์สามารถแรสเตอร์พิกเซลเป็นพื้นผิวเดียวในขนาดเต็มของวิวพอร์ตการแสดงผลและส่งพื้นผิวนั้นไปยัง Viz เพื่อแสดงผล ตัวประมวลผลการแสดงผลจะต้องคัดลอกพิกเซลจากพื้นผิวเดียวนั้นไปยังตำแหน่งที่เหมาะสมในบัฟเฟอร์เฟรม (ตัวอย่างเช่น หน้าจอ) อย่างไรก็ตาม หากตัวประกอบนั้นต้องการอัปเดตแม้แต่พิกเซลเดียว ก็จะต้องแรสเตอร์วิวพอร์ตแบบเต็มอีกครั้งและส่งพื้นผิวใหม่ไปยัง Viz

แต่วิวพอร์ตจะถูกแบ่งออกเป็นชิ้นส่วน ชิ้นส่วนพื้นผิว GPU ที่แยกต่างหากจะอยู่หลังแต่ละชิ้นส่วนที่มีพิกเซลแรสเตอร์สำหรับส่วนหนึ่งของวิวพอร์ต จากนั้นโหมดแสดงภาพสามารถอัปเดตการ์ดแต่ละใบหรือแม้กระทั่งเปลี่ยนตำแหน่งบนหน้าจอของการ์ดที่มีอยู่ก็ได้ ตัวอย่างเช่น เมื่อเลื่อนเว็บไซต์ ตำแหน่งของชิ้นส่วนที่มีอยู่จะเลื่อนขึ้น และในบางครั้งก็จำเป็นต้องแรสเตอร์การ์ดใหม่สำหรับเนื้อหาที่อยู่ด้านล่างของหน้า

4 การ์ด

รูปภาพด้านบนแสดงภาพวันที่มีแดดจัด ซึ่งประกอบด้วย 4 ไทล์ เมื่อมีการเลื่อน การ์ดที่ 5 จะเริ่มปรากฏขึ้น การ์ดใบหนึ่งมีเพียงสีเดียว (สีฟ้า) และมีวิดีโอและ iframe อยู่ด้านบน ซึ่งจะนำไปสู่หัวข้อถัดไป

ควอดและพื้นผิว

ชิ้นส่วนพื้นผิว GPU เป็น quad ชนิดพิเศษ ซึ่งเป็นชื่อที่ใช้ง่ายสำหรับพื้นผิวหมวดหมู่ใดหมวดหมู่หนึ่ง สี่เหลี่ยมจัตุรัสจะระบุพื้นผิวอินพุต รวมถึงระบุวิธีเปลี่ยนรูปแบบและใช้เอฟเฟกต์ภาพกับภาพดังกล่าว ตัวอย่างเช่น ชิ้นส่วนเนื้อหาปกติจะมีการเปลี่ยนรูปแบบที่ระบุตำแหน่ง x และ y ในตารางกริดของไทล์

ชิ้นส่วนพื้นผิว GPU

การ์ดแรสเตอร์เหล่านี้จะรวมอยู่ในผ่านการแสดงผล ซึ่งเป็นรายการสี่เหลี่ยมจัตุรัส การ์ดแสดงผลไม่มีข้อมูลพิกเซล แต่มีคำแนะนำเกี่ยวกับตำแหน่งและวิธีวาดสี่เหลี่ยมจัตุรัสแต่ละรูปเพื่อสร้างเอาต์พุตพิกเซลที่ต้องการ มีสี่เหลี่ยมจัตุรัสในการวาดสำหรับชิ้นส่วนพื้นผิว 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 สำหรับแต่ละรายการ
      • สี่เหลี่ยมจตุรัสพื้นผิว: แบบมีรหัส 2, วาดโดยใช้มาตราส่วนและแปลงการแปลง
  • แพลตฟอร์ม UI ของเบราว์เซอร์: ID=1
    • แสดงผลผ่าน 0: วาดเพื่อให้แสดงผล
      • วาดรูปสี่เหลี่ยมสำหรับ UI ของเบราว์เซอร์ (ไทล์ด้วย)
  • bar.com/index.html แพลตฟอร์ม: ID=2
    • แสดงผลผ่าน 0: วาดเพื่อให้แสดงผล
      • วาดสี่เหลี่ยมสำหรับเนื้อหาของ iframe #two โดยมีตำแหน่ง x และ y สำหรับแต่ละรายการ

บทสรุป

ขอขอบคุณที่อ่าน และเมื่อรวมกับ 2 โพสต์ก่อนหน้า ก็สรุปภาพรวมของ RenderingNG ได้ ถัดไปจะเป็นเจาะลึกเกี่ยวกับความท้าทายและเทคโนโลยีภายในองค์ประกอบย่อยหลายๆ องค์ประกอบของไปป์ไลน์การแสดงภาพ ตั้งแต่ต้นจนจบ สิ่งเหล่านี้จะพร้อมให้บริการในเร็วๆ นี้

ภาพโดย Una Kravets