โมเดลเลเยอร์
บทนำ
สำหรับนักพัฒนาเว็บส่วนใหญ่ โมเดลพื้นฐานของหน้าเว็บคือ DOM การแสดงภาพมักเป็นขั้นตอนที่ไม่ชัดเจนในการเปลี่ยนภาพแทนของหน้าให้เป็นรูปภาพบนหน้าจอ เบราว์เซอร์สมัยใหม่ได้เปลี่ยนวิธีการทำงานของการแสดงผลในไม่กี่ปีที่ผ่านมาเพื่อใช้ประโยชน์จากการ์ดแสดงผล ซึ่งมักเรียกกันอย่างคลุมเครือว่า "การเร่งความเร็วด้วยฮาร์ดแวร์" เมื่อพูดถึงหน้าเว็บปกติ (ที่ไม่ใช่ Canvas2D หรือ WebGL) คำนั้นหมายความว่าอย่างไร บทความนี้อธิบายโมเดลพื้นฐานที่สนับสนุนการแสดงผลเนื้อหาเว็บที่มีการเร่งฮาร์ดแวร์ใน Chrome
ข้อควรระวังเรื่องไขมันสูง
เราพูดถึง WebKit ในที่นี้ และโดยเฉพาะอย่างยิ่งเราพูดถึงพอร์ต Chromium ของ WebKit บทความนี้กล่าวถึงรายละเอียดการใช้งาน Chrome ไม่ใช่ฟีเจอร์ของแพลตฟอร์มบนเว็บ แพลตฟอร์มเว็บและมาตรฐานไม่ได้กำหนดรายละเอียดการติดตั้งใช้งานในระดับนี้ ดังนั้นจึงไม่รับประกันว่าบทความนี้จะนำไปปรับใช้กับเบราว์เซอร์อื่นได้ แต่ความรู้เกี่ยวกับข้อมูลภายในจะมีประโยชน์สำหรับการแก้ไขข้อบกพร่องและการปรับปรุงประสิทธิภาพขั้นสูง
นอกจากนี้ โปรดทราบว่าบทความนี้ทั้งหมดพูดถึงสถาปัตยกรรมการแสดงผลหลักของ Chrome ที่เปลี่ยนแปลงไปอย่างรวดเร็ว บทความนี้พยายามครอบคลุมเฉพาะสิ่งที่ไม่น่าจะเปลี่ยนแปลง แต่ไม่ได้รับประกันว่าข้อมูลทั้งหมดจะยังคงมีผลบังคับใช้ในอีก 6 เดือน
คุณควรเข้าใจว่า Chrome มีเส้นทางการแสดงผล 2 แบบมาระยะหนึ่งแล้ว ได้แก่ เส้นทางที่เร่งการแสดงผลด้วยฮาร์ดแวร์และเส้นทางซอฟต์แวร์แบบเก่า ขณะที่เขียนหน้านี้ หน้าเว็บทั้งหมดจะมุ่งเน้นการเร่งฮาร์ดแวร์ใน Windows, ChromeOS และ Chrome สำหรับ Android สำหรับ Mac และ Linux เฉพาะหน้าเว็บที่ต้องใช้การประกอบเนื้อหาบางส่วนลงในเส้นทางที่เร่งรัดจนเกินไป (ดูข้อมูลเพิ่มเติมด้านล่างว่าต้องใส่องค์ประกอบใด) แต่อีกไม่นานทุกหน้าก็จะไปอยู่ในเส้นทางที่มีการเร่งความเร็วด้วยเช่นกัน
สุดท้ายนี้ เราจะดูเบื้องหลังของเครื่องมือแสดงภาพและดูฟีเจอร์ต่างๆ ที่ส่งผลต่อประสิทธิภาพอย่างมาก เมื่อพยายามปรับปรุงประสิทธิภาพของเว็บไซต์ การทำความเข้าใจโมเดลเลเยอร์ก็อาจมีประโยชน์ แต่การลองถ่ายภาพด้วยตัวเราเองก็ทำได้ง่ายเช่นกัน เนื่องจากเลเยอร์เป็นโครงสร้างที่มีประโยชน์ แต่การสร้างจำนวนมากอาจทำให้โอเวอร์เฮดทั่วทั้งสแต็กกราฟิกได้ เราเตือนคุณล่วงหน้าแล้ว
จาก DOM ไปยังหน้าจอ
ขอแนะนำเลเยอร์
เมื่อโหลดและแยกวิเคราะห์หน้าเว็บแล้ว หน้าเว็บดังกล่าวจะแสดงเป็นโครงสร้างในเบราว์เซอร์ที่นักพัฒนาเว็บหลายคนคุ้นเคย ซึ่งก็คือ DOM อย่างไรก็ตาม เมื่อแสดงผลหน้าเว็บ เบราว์เซอร์จะมีชุดตัวแทนระดับกลางที่ไม่ปรากฏต่อนักพัฒนาซอฟต์แวร์โดยตรง องค์ประกอบที่สำคัญที่สุดของโครงสร้างเหล่านี้คือเลเยอร์
จริงๆ แล้วใน Chrome จะมีเลเยอร์อยู่หลายประเภท ได้แก่ เครือข่าย RenderLayers ซึ่งเป็นเลเยอร์ย่อยของ DOM และ GraphicsParameters สิ่งหลังน่าสนใจที่สุดสำหรับเราที่นี่ เนื่องจาก GraphicsLayers เป็นพื้นผิวที่จะอัปโหลดไปยัง GPU ในที่นี้เราจะพูดว่า "เลเยอร์" ต่อไปหมายถึง GraphicsLayer
มองข้ามคำศัพท์เกี่ยวกับ GPU: พื้นผิวคืออะไร ให้คิดว่านี่เป็นรูปภาพบิตแมปที่ย้ายจากหน่วยความจำหลัก (เช่น RAM) ไปยังหน่วยความจำวิดีโอ (เช่น VRAM ใน GPU) เมื่อกราฟิกอยู่บน GPU คุณสามารถแมปกับเรขาคณิตแบบตาข่ายในวิดีโอเกมหรือโปรแกรม CAD เทคนิคนี้จะถูกนำไปใช้ในการสร้าง "สกิน" โมเดลโครงกระดูก 3 มิติ Chrome ใช้พื้นผิวเพื่อนำเนื้อหาของหน้าเว็บส่วนหนึ่งมาไว้บน GPU คุณสามารถจับคู่พื้นผิวกับตำแหน่งและการแปลงต่างๆ ได้โดยใช้ราคาถูกลง นี่คือวิธีการทำงานของ 3D CSS และยังเป็นวิธีที่ยอดเยี่ยมสำหรับการเลื่อนอย่างรวดเร็ว แต่จะมีมากกว่าในทั้ง 2 วิธีนี้ในภายหลัง
มาดูตัวอย่าง 2-3 ข้อเพื่ออธิบายแนวคิดเลเยอร์กัน
เครื่องมือที่มีประโยชน์มากเมื่อศึกษาเลเยอร์ใน Chrome คือธง "แสดงเส้นขอบของเลเยอร์แบบผสม" ในการตั้งค่า (เช่น ไอคอนฟันเฟืองขนาดเล็ก) ในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ ใต้ส่วนหัว "การแสดงผล" ซึ่งเพียงแค่ไฮไลต์ตำแหน่งต่างๆ บนหน้าจอ มาเปิดกัน ภาพหน้าจอและตัวอย่างเหล่านี้มาจาก 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>
การใส่พร็อพเพอร์ตี้ 3D CSS ใน <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 เหตุการณ์ คือ เหตุการณ์หนึ่งสำหรับเลเยอร์ และอีกเหตุการณ์หนึ่งสำหรับปุ่มเอง ซึ่งจะมีการทาสีใหม่เมื่อมีการเปลี่ยนแปลง/จากสถานะหดหู่
โปรดทราบว่า Chrome ไม่จำเป็นต้องทาสีเลเยอร์ทั้งหมดใหม่อีกครั้ง โดยจะพยายามปรับปรุงเฉพาะส่วนต่างๆ ของ DOM ที่เสียหายขึ้นมาใหม่เท่านั้น ในกรณีนี้ องค์ประกอบ DOM ที่เราแก้ไขคือขนาดของทั้งเลเยอร์ แต่ในหลายกรณี เลเยอร์หนึ่งจะมีองค์ประกอบ DOM อยู่เป็นจำนวนมาก
คำถามถัดไปที่เห็นได้ชัดคือสาเหตุของการใช้งานไม่ได้และบังคับให้รีสตาร์ท ข้อนี้ยากที่จะตอบให้ละเอียดถี่ถ้วน เนื่องจากมีกรณีสุดโต่งจำนวนมากที่สามารถบังคับให้เกิดการโมฆะได้ สาเหตุที่พบบ่อยที่สุดคือการทำให้ DOM สกปรกโดยการควบคุมรูปแบบ CSS หรือทำให้มีการส่งต่อ Tony Gentilcore มีบล็อกโพสต์ดีๆ เกี่ยวกับสาเหตุของการส่งต่อ และ Stoyan Stefanov มีบทความที่กล่าวถึงการวาดภาพโดยละเอียดยิ่งขึ้น (แต่จบลงที่แค่ภาพวาดเท่านั้น ไม่ใช่การประกอบภาพสวยๆ)
วิธีที่ดีที่สุดในการพิจารณาว่าปัญหานี้ส่งผลกระทบต่อสิ่งที่คุณกำลังดำเนินการอยู่หรือไม่ คือการใช้ไทม์ไลน์ของเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์และเครื่องมือแสดงภาพสี่เหลี่ยมผืนผ้า เพื่อดูว่าคุณจะทาสีใหม่เมื่อใดหรือไม่ จากนั้นลองระบุตำแหน่งที่คุณแก้ไข DOM ก่อนการส่งต่อ/ทาสีใหม่ หากการวาดภาพเป็นสิ่งที่หลีกเลี่ยงไม่ได้แต่ดูเหมือนจะใช้เวลานานจนไม่สมเหตุสมผล โปรดอ่านบทความของ Eberhard Gräther เกี่ยวกับโหมดการลงสีแบบต่อเนื่องในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์
นำมาประกอบกัน: DOM ไปยังหน้าจอ
แล้ว Chrome จะเปลี่ยน DOM เป็นรูปภาพหน้าจอได้อย่างไร โดยหลักการแล้ว:
- นำ DOM และแยกออกเป็นเลเยอร์ต่างๆ
- ทาสีแต่ละเลเยอร์เหล่านี้เป็นซอฟต์แวร์บิตแมปแยกกันอย่างอิสระ
- อัปโหลดไปยัง GPU เป็นพื้นผิว
- นำเลเยอร์ต่างๆ มาประกอบเข้าด้วยกันเป็นรูปภาพหน้าจอสุดท้าย
ซึ่งทั้งหมดจะต้องเกิดขึ้นในครั้งแรกที่ Chrome สร้างเฟรมของหน้าเว็บ แต่อาจต้องใช้ทางลัดบางอย่างสำหรับเฟรมในอนาคต ดังนี้
- หากคุณสมบัติ CSS บางรายการมีการเปลี่ยนแปลง คุณก็ไม่จำเป็นต้องทาสีใหม่ Chrome สามารถคอมไพล์เลเยอร์เดิมที่มีอยู่แล้วบน GPU ได้อีกครั้งโดยเป็นพื้นผิว แต่มีคุณสมบัติในการจัดวางองค์ประกอบแตกต่างกัน (เช่น ในตำแหน่งต่างๆ มีความทึบแสงที่แตกต่างกัน เป็นต้น)
- หากบางส่วนของเลเยอร์ถูกยกเลิก เลเยอร์นั้นจะได้รับการทาสีใหม่และอัปโหลดใหม่ หากเนื้อหายังคงเหมือนเดิมแต่แอตทริบิวต์แบบผสมมีการเปลี่ยนแปลง (เช่น มีการแปลหรือความทึบแสงเปลี่ยนไป) Chrome สามารถปล่อยเนื้อหาไว้ใน GPU และทำ compo ใหม่เพื่อสร้างเฟรมใหม่
ดังที่ตอนนี้ควรจะชัดเจนแล้ว โมเดลการประกอบภาพแบบเลเยอร์มีผลอย่างมากต่อประสิทธิภาพการแสดงผล การประกอบภาพมีราคาถูกเมื่อไม่ต้องลงสีพื้นเลย ดังนั้นการเลี่ยงการทาสีเลเยอร์ใหม่จึงถือเป็นเป้าหมายโดยรวมที่ดีเมื่อพยายามแก้ไขข้อบกพร่องของประสิทธิภาพการแสดงผล นักพัฒนาซอฟต์แวร์ที่มีความชำนาญจะตรวจสอบรายการทริกเกอร์การประกอบหน้าเว็บข้างต้น และตระหนักว่าสามารถบังคับให้สร้างเลเยอร์ได้โดยง่าย แต่จงระวังการสร้างโค้ดโดยการปิดบัง เนื่องจากโค้ดดังกล่าวไม่ได้มีที่ว่างอยู่ เพราะโค้ดจะกินหน่วยความจำใน RAM ของระบบและใน GPU (โดยเฉพาะอย่างยิ่งในอุปกรณ์เคลื่อนที่) และการมีจำนวนมากอาจทำให้มีการใช้โอเวอร์เฮดอื่นๆ ในลอจิกที่จะติดตามสิ่งที่มองเห็นได้ เลเยอร์จำนวนมากยังเพิ่มเวลาในการแรสเตอร์ได้หากเลเยอร์ดังกล่าวมีขนาดใหญ่และทับซ้อนกันมากซึ่งไม่เคยเกิดขึ้นมาก่อน ซึ่งบางครั้งเรียกว่า "การวาดทับ" ดังนั้นโปรดใช้ความรู้อย่างชาญฉลาด
เท่านี้เองครับ คอยติดตามบทความเพิ่มเติมอีก 2-3 เรื่องเกี่ยวกับนัยยะในทางปฏิบัติของโมเดลเลเยอร์