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

มาดูโครงสร้างข้อมูลหลักกัน ซึ่งเป็นอินพุตและเอาต์พุตของไปป์ไลน์การแสดงผล

โครงสร้างข้อมูลเหล่านี้ ได้แก่

  • เฟรมทรีประกอบด้วยโหนดในเครื่องและโหนดระยะไกลที่แสดงถึงเว็บเอกสารที่อยู่ในกระบวนการแสดงผลและโปรแกรมแสดงผล 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 เฟรม ดังนี้

เฟรมหลัก 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 รายการ แต่ตอนนี้ก็มีเศษส่วนของเฟรมเทรียร์ในเครื่อง 3 รายการ โดย 2 รายการอยู่ในกระบวนการแสดงผลสำหรับ foo.com และ 1 รายการอยู่ในกระบวนการแสดงผลสำหรับ bar.com ดังนี้

การนําเสนอภาพเรนเดอร์ 2 ภาพและเศษส่วนของต้นไม้เฟรม 3 รายการ

หากต้องการสร้างเฟรมคอมโพสิต 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 เพื่อแสดงรายการรายการที่แสดง

เช่น

กล่องสีน้ำเงินที่มีคำว่า &quot;Hello World&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 และข้อความ "Hello world"

รายการรายการที่แสดงจะเรียงจากหลังไปหน้า ในตัวอย่างด้านบน div สีเขียวอยู่ก่อน div สีน้ำเงินตามลําดับ DOM แต่ลําดับการวาดของ CSS กําหนดให้ div สีน้ำเงินที่มี z-index ติดลบต้องวาดก่อน (ขั้นตอนที่ 3) div สีเขียว (ขั้นตอนที่ 4.1) รายการที่แสดงจะสอดคล้องกับขั้นตอนพื้นฐานของข้อกำหนดลำดับการวาด CSS โดยคร่าวๆ องค์ประกอบ DOM รายการเดียวอาจส่งผลให้มีรายการที่แสดงหลายรายการ เช่น #green มีรายการที่แสดงสำหรับพื้นหลังและรายการที่แสดงอีกรายการสำหรับข้อความย่อย ความละเอียดนี้สำคัญต่อการนำเสนอความซับซ้อนทั้งหมดของข้อกำหนดลำดับการวาด CSS เช่น การแทรกสลับที่เกิดจากระยะขอบลบ

สี่เหลี่ยมผืนผ้าสีเขียวที่มีกล่องสีเทาวางซ้อนอยู่บางส่วนและคำว่า &quot;Hello world&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 และข้อความ "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 และข้อความ "ฉันกำลังตก"

ต้นไม้พร็อพเพอร์ตี้การเปลี่ยนรูปแบบและกลุ่มการวาดภาพจะเป็นดังนี้ (ย่อเพื่อความกระชับ)

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

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

ตัวอย่างก่อนหน้านี้ควรสร้างเลเยอร์แบบคอมโพสิต 2 เลเยอร์ ดังนี้

  • เลเยอร์คอมโพสิตขนาด 800x600 ที่มีคำสั่งวาด ดังนี้
    1. drawRect ขนาด 800x600 และสีขาว
    2. drawRect ขนาด 100x100 ที่ตําแหน่ง 0,0 และสีชมพู
  • เลเยอร์คอมโพสิตขนาด 144x224 ที่มีคำสั่งวาด ดังนี้
    1. drawTextBlob ที่มีตําแหน่ง 0,0 และข้อความ "Hello world"
    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 scale, document clip, root, document scroll

เฟรมคอมโพสิต: พื้นผิว พื้นผิวการแสดงผล และไทล์พื้นผิว GPU

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

การ์ด

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

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

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

รูปสี่เหลี่ยมจัตุรัสและพื้นผิว

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

ชิ้นส่วนพื้นผิวของ GPU

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

ภาพโดย Una Kravets