การแสดงผลแบบเร่งใน Chrome

โมเดลเลเยอร์

Tom Wiltzius
Tom Wiltzius

เกริ่นนำ

สำหรับนักพัฒนาเว็บส่วนใหญ่ โมเดลพื้นฐานของหน้าเว็บคือ DOM การแสดงผลเป็นขั้นตอนที่มักคลุมเครือในการเปลี่ยนการนำเสนอหน้าเว็บให้เป็นรูปภาพบนหน้าจอ เบราว์เซอร์สมัยใหม่ได้เปลี่ยนวิธีการทำงานของการแสดงผลในช่วงไม่กี่ปีที่ผ่านมาเพื่อใช้ประโยชน์จากการ์ดแสดงผล ซึ่งมักเรียกอย่างไม่ชัดเจนว่าเป็น "การเร่งฮาร์ดแวร์" เมื่อพูดถึงหน้าเว็บปกติ (ไม่ใช่ Canvas2D หรือ WebGL) คำดังกล่าวหมายถึงอะไร บทความนี้อธิบายโมเดลพื้นฐานที่สนับสนุนการแสดงผลเนื้อหาเว็บแบบเร่งด้วยฮาร์ดแวร์ใน Chrome

ข้อควรระวังขนาดใหญ่สำหรับไขมัน

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

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

คุณควรเข้าใจว่า Chrome มีวิธีการแสดงผลที่แตกต่างกัน 2 เส้นทางมาระยะหนึ่งแล้ว ได้แก่ เส้นทางที่ใช้การเร่งฮาร์ดแวร์และเส้นทางของซอฟต์แวร์แบบเก่า จากการเขียนนี้ หน้าเว็บทั้งหมดจะไล่ตามเส้นทางที่ใช้ฮาร์ดแวร์เร่งการทำงานใน Windows, ChromeOS และ Chrome สำหรับ Android บน Mac และ Linux จะมีเพียงหน้าเว็บที่ต้องมีการจัดส่วนประกอบสำหรับเนื้อหาบางอย่างเท่านั้นที่จะลงไปตามเส้นทางแบบเร่ง (ดูข้อมูลเพิ่มเติมด้านล่างเกี่ยวกับสิ่งที่ต้องมีการจัดองค์ประกอบ) แต่ในไม่ช้านี้ หน้าเว็บทั้งหมดก็จะเข้าสู่กระบวนการแบบเร่งด้วยเช่นกัน

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

จาก DOM ไปยังหน้าจอ

ขอแนะนำเลเยอร์

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

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

นอกเหนือจากคำศัพท์ของ GPU แล้ว พื้นผิวคืออะไร ภาพนี้เป็นภาพบิตแมปที่ย้ายจากหน่วยความจำหลัก (เช่น RAM) ไปยังหน่วยความจำวิดีโอ (เช่น VRAM บน GPU) เมื่ออยู่ใน GPU แล้ว คุณสามารถแมปโมเดลนั้นกับเรขาคณิตแบบตาข่าย (Mesh) ในวิดีโอเกมหรือโปรแกรม CAD โดยใช้เทคนิคนี้เพื่อสร้าง "สกิน" ของโมเดลโครงร่าง 3 มิติโดย Chrome ใช้พื้นผิวเพื่อแบ่งเนื้อหาของหน้าเว็บเป็นส่วนๆ บน GPU คุณสามารถจับคู่พื้นผิวกับตำแหน่งและการแปลงต่างๆ ในราคาที่ไม่แพงด้วยการนำไปใช้กับตาข่ายสี่เหลี่ยมผืนผ้าแบบเรียบง่าย นี่คือวิธีการทำงานของ 3D CSS และยังเป็นผลดีสำหรับการเลื่อนอย่างรวดเร็ว แต่เพิ่มเติมเกี่ยวกับทั้ง 2 วิธีนี้ในภายหลัง

มาดูตัวอย่างบางส่วนเพื่อแสดงถึงแนวคิดของเลเยอร์กัน

เครื่องมือที่มีประโยชน์มากในการศึกษาเลเยอร์ใน Chrome คือ Flag "แสดงเส้นขอบของเลเยอร์แบบผสม" ในการตั้งค่า (เช่น ไอคอนฟันเฟืองเล็กๆ) ใน Dev Tools ใต้ส่วนหัว "การแสดงผล" เพียงแต่ไฮไลต์จุดที่เลเยอร์อยู่บนหน้าจอ มาเริ่มกันเลย ภาพหน้าจอและตัวอย่างเหล่านี้นำมาจาก Chrome Canary เวอร์ชันล่าสุดหรือ Chrome 27 ตอนที่เขียนบทความนี้

รูปที่ 1: หน้าแบบเลเยอร์เดียว

<!doctype html>
<html>
<body>
  <div>I am a strange root.</div>
</body>
</html>
ภาพหน้าจอของการแสดงเส้นขอบของเลเยอร์แบบผสมรอบเลเยอร์ฐานของหน้าเว็บ
ภาพหน้าจอของเลเยอร์แสดงผลเลเยอร์แบบผสมรอบเลเยอร์ฐานของหน้าเว็บ

หน้านี้มีเพียงเลเยอร์เดียว ตารางกริดสีฟ้าคือชิ้นส่วนต่างๆ ที่คุณอาจมองได้ว่าเป็นหน่วยย่อยของเลเยอร์ที่ Chrome ใช้ในการอัปโหลดส่วนต่างๆ ของเลเยอร์ขนาดใหญ่ไปยัง GPU ในแต่ละครั้ง ข้อความพวกนี้ไม่สำคัญนัก

รูปที่ 2: องค์ประกอบในเลเยอร์ของตนเอง

<!doctype html>
<html>
<body>
  <div style="transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
    I am a strange root.
  </div>
</body>
</html>
ภาพหน้าจอของเส้นขอบการแสดงภาพของเลเยอร์ที่หมุน
ภาพหน้าจอของเส้นขอบการแสดงผลของเลเยอร์ที่หมุนแล้ว

เมื่อวางคุณสมบัติ CSS แบบ 3 มิติใน <div> ที่หมุน เราจะเห็นว่าองค์ประกอบมีลักษณะอย่างไรเมื่อองค์ประกอบได้รับเลเยอร์ของตัวเอง เช่น สังเกตเส้นขอบสีส้มซึ่งจะร่างเลเยอร์ในมุมมองนี้

เกณฑ์การสร้างเลเยอร์

มีอะไรอีกบ้างที่มีเลเยอร์ของตัวเอง การเรียนรู้ของ Chrome ในที่นี้ได้เปลี่ยนแปลงไปตามกาลเวลาและยังคงเกิดขึ้นอย่างต่อเนื่อง แต่ในปัจจุบัน วิธีการสร้างเลเยอร์ทริกเกอร์ต่อไปนี้

  • คุณสมบัติ CSS ที่เปลี่ยนรูปแบบแบบ 3 มิติหรือมุมมอง
  • องค์ประกอบ <video> รายการกำลังใช้การถอดรหัสวิดีโอแบบเร่ง
  • <canvas> องค์ประกอบที่มีบริบท 3 มิติ (WebGL) หรือบริบท 2 มิติแบบเร่ง
  • ปลั๊กอินแบบผสม (เช่น Flash)
  • องค์ประกอบที่มีภาพเคลื่อนไหว CSS สำหรับความทึบแสงหรือการใช้การเปลี่ยนรูปแบบที่เคลื่อนไหว
  • องค์ประกอบที่มีตัวกรอง CSS แบบเร่ง
  • องค์ประกอบมีองค์ประกอบสืบทอดที่มีเลเยอร์ประกอบกัน (กล่าวคือ หากองค์ประกอบมีองค์ประกอบย่อยที่อยู่ในเลเยอร์ของตัวเอง)
  • องค์ประกอบมีกลุ่มข้างเคียงที่มีดัชนีลำดับ Z ต่ำกว่าซึ่งมีเลเยอร์ประกอบกัน (กล่าวคือ องค์ประกอบดังกล่าวแสดงผลทับเลเยอร์แบบผสม)

ผลกระทบในทางปฏิบัติ: แอนิเมชัน

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

รูปที่ 3: เลเยอร์ภาพเคลื่อนไหว

<!doctype html>
<html>
<head>
  <style>
  div {
    animation-duration: 5s;
    animation-name: slide;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    width: 200px;
    height: 200px;
    margin: 100px;
    background-color: gray;
  }
  @keyframes slide {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(120deg);
    }
  }
  </style>
</head>
<body>
  <div>I am a strange root.</div>
</body>
</html>

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

ตัวอย่างเช่น มุมมองนี้สำหรับไทม์ไลน์เครื่องมือสำหรับนักพัฒนาเว็บ ไม่มีการระบายสีขณะที่เลเยอร์นี้หมุนกลับไปกลับมา

ภาพหน้าจอของไทม์ไลน์เครื่องมือสำหรับนักพัฒนาเว็บระหว่างภาพเคลื่อนไหว
ภาพหน้าจอของไทม์ไลน์เครื่องมือสำหรับนักพัฒนาเว็บระหว่างภาพเคลื่อนไหว

ไม่ถูกต้อง! การทาสีซ้ำ

แต่หากเนื้อหาของเลเยอร์มีการเปลี่ยนแปลง ก็จะต้องทำการทาสีใหม่

รูปที่ 4: การทำซ้ำเลเยอร์

<!doctype html>
<html>
<head>
  <style>
  div {
    animation-duration: 5s;
    animation-name: slide;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    width: 200px;
    height: 200px;
    margin: 100px;
    background-color: gray;
  }
  @keyframes slide {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(120deg);
    }
  }
  </style>
</head>
<body>
  <div id="foo">I am a strange root.</div>
  <input id="paint" type="button" value="repaint">
  <script>
    var w = 200;
    document.getElementById('paint').onclick = function() {
      document.getElementById('foo').style.width = (w++) + 'px';
    }
  </script>
</body>
</html>

ทุกครั้งที่มีการคลิกองค์ประกอบอินพุต องค์ประกอบที่หมุนจะกว้างขึ้น 1 พิกเซล วิธีนี้ทำให้เกิดการส่งต่อและการทำซ้ำองค์ประกอบทั้งหมด ซึ่งในกรณีนี้คือทั้งเลเยอร์

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

ภาพหน้าจอของช่องทำเครื่องหมายการแสดงสีรูปสี่เหลี่ยมผืนผ้า
ภาพหน้าจอของช่องทำเครื่องหมายสำหรับแสดงรูปสี่เหลี่ยมผืนผ้า

เหตุการณ์การแสดงผลจะปรากฏในไทม์ไลน์เครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ด้วย ผู้อ่านที่ตาคมชัดอาจสังเกตเห็นว่ามีเหตุการณ์การระบายสี 2 เหตุการณ์ ได้แก่ เหตุการณ์หนึ่งสำหรับเลเยอร์ และอีกเหตุการณ์หนึ่งสำหรับปุ่มนั้นเอง ซึ่งจะมีการทาสีใหม่เมื่อมีการเปลี่ยนแปลงเป็น/จากสถานะซึมเศร้า

ภาพหน้าจอของไทม์ไลน์ของเครื่องมือสําหรับนักพัฒนาเว็บที่ทาสีเลเยอร์ใหม่
ภาพหน้าจอของไทม์ไลน์ของเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ที่วาดเลเยอร์ใหม่

โปรดทราบว่า Chrome ไม่จำเป็นต้องวาดใหม่ทั้งเลเยอร์เสมอไป แต่จะพยายามทำได้อย่างชาญฉลาดในการแทนที่เฉพาะส่วนของ DOM ที่ใช้งานไม่ได้เท่านั้น ในกรณีนี้ องค์ประกอบ DOM ที่เราแก้ไขคือขนาดของเลเยอร์ทั้งเลเยอร์ แต่ในหลายๆ กรณี จะมีองค์ประกอบ DOM จำนวนมากในเลเยอร์

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

วิธีที่ดีที่สุดในการดูว่าสิ่งนั้นส่งผลกระทบต่อสิ่งที่คุณกำลังดำเนินการอยู่หรือไม่ คือใช้เครื่องมือไทม์ไลน์ของเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์และเครื่องมือแสดง Paint Rects เพื่อดูว่ากำลังทาสีใหม่เมื่อต้องการหรือไม่ แล้วลองหาตำแหน่งที่คุณกำจัด DOM ก่อน แล้วรีเลย์/ตกแต่งภาพใหม่ หากการวาดภาพนั้นหลีกเลี่ยงไม่ได้ แต่ดูเหมือนว่าจะใช้เวลานานอย่างไม่สมเหตุผล โปรดอ่านบทความของ Eberhard Gräther เกี่ยวกับโหมดการวาดภาพต่อเนื่องในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์

กำลังประกอบเข้าด้วยกัน: DOM ไปยังหน้าจอ

แล้ว Chrome จะเปลี่ยน DOM ให้เป็นรูปภาพบนหน้าจอได้อย่างไร โดยหลักแล้วก็คือ

  1. นำ DOM และแยกออกเป็นเลเยอร์
  2. ลงสีแต่ละเลเยอร์เหล่านี้แยกกันลงในบิตแมปของซอฟต์แวร์
  3. อัปโหลดไปยัง GPU เป็นพื้นผิว
  4. ผสมผสานเลเยอร์ต่างๆ เข้าด้วยกันเป็นภาพหน้าจอสุดท้าย

ซึ่งทั้งหมดนี้จะเกิดขึ้นตั้งแต่ครั้งแรกที่ Chrome สร้างเฟรมของหน้าเว็บ แต่ก็สามารถใช้ทางลัดบางอย่างสำหรับเฟรมในอนาคตได้

  1. หากคุณสมบัติ CSS บางรายการเปลี่ยนไป คุณไม่จําเป็นต้องทําสีใหม่ Chrome สามารถแยกเลเยอร์เดิมที่มีอยู่แล้วบน GPU เป็นพื้นผิว แต่มีคุณสมบัติในการประกอบที่แตกต่างกัน (เช่น อยู่ในตำแหน่งต่างๆ และความทึบแสงที่แตกต่างกัน)
  2. หากเลเยอร์บางส่วนใช้งานไม่ได้ ระบบจะแสดงผลซ้ำและอัปโหลดซ้ำ หากเนื้อหายังเหมือนเดิมแต่แอตทริบิวต์ที่ทำการ Composite เปลี่ยนไป (เช่น มีการแปลหรือมีการเปลี่ยนแปลงความทึบแสง) Chrome อาจปล่อย API นี้ไว้บน GPU และประกอบใหม่เพื่อสร้างเฟรมใหม่

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

จบแล้วสำหรับวันนี้ โปรดติดตามบทความเพิ่มเติมอีก 2-3 บทความเกี่ยวกับผลกระทบในทางปฏิบัติของโมเดลเลเยอร์

แหล่งข้อมูลเพิ่มเติม