รูปแบบเลเยอร์
บทนำ
สําหรับนักพัฒนาเว็บส่วนใหญ่ รูปแบบพื้นฐานของหน้าเว็บคือ DOM การแสดงผลเป็นกระบวนการที่มักคลุมเครือในการเปลี่ยนการนําเสนอหน้าเว็บนี้ให้เป็นภาพบนหน้าจอ เบราว์เซอร์สมัยใหม่ได้เปลี่ยนแปลงวิธีการแสดงผลในช่วงไม่กี่ปีที่ผ่านมาเพื่อใช้ประโยชน์จากการ์ดกราฟิก ซึ่งมักเรียกอย่างคลุมเครือว่า "การเร่งด้วยฮาร์ดแวร์" เมื่อพูดถึงหน้าเว็บปกติ (ไม่ใช่ Canvas2D หรือ WebGL) คำว่า "การเร่งด้วยฮาร์ดแวร์" นั้นหมายถึงอะไรกันแน่ บทความนี้จะอธิบายรูปแบบพื้นฐานที่รองรับการแสดงผลเนื้อหาเว็บที่เร่งด้วยฮาร์ดแวร์ใน Chrome
ข้อควรระวังที่สำคัญ
เรากำลังพูดถึง WebKit และเจาะจงลงไปคือพอร์ต Chromium ของ WebKit บทความนี้กล่าวถึงรายละเอียดการใช้งาน Chrome ไม่ใช่ฟีเจอร์แพลตฟอร์มเว็บ แพลตฟอร์มและมาตรฐานเว็บไม่ได้กำหนดรายละเอียดการใช้งานในระดับนี้ ดังนั้นจึงไม่มีการรับประกันว่าข้อมูลในบทความนี้จะใช้ได้กับเบราว์เซอร์อื่นๆ แต่ความรู้เกี่ยวกับข้อมูลภายในก็มีประโยชน์สำหรับการแก้ไขข้อบกพร่องขั้นสูงและการปรับแต่งประสิทธิภาพ
นอกจากนี้ โปรดทราบว่าบทความนี้กล่าวถึงส่วนสำคัญของสถาปัตยกรรมการแสดงผลของ Chrome ซึ่งเปลี่ยนแปลงอย่างรวดเร็ว บทความนี้พยายามอธิบายเฉพาะสิ่งที่มีแนวโน้มจะไม่เปลี่ยนแปลง แต่ไม่ได้รับประกันว่าข้อมูลทั้งหมดจะยังคงใช้ได้ในอีก 6 เดือนข้างหน้า
คุณต้องเข้าใจว่า Chrome มีเส้นทางการแสดงผล 2 เส้นทางมาระยะหนึ่งแล้ว ได้แก่ เส้นทางที่เร่งด้วยฮาร์ดแวร์และเส้นทางซอฟต์แวร์แบบเก่า ขณะเขียนบทความนี้ หน้าเว็บทั้งหมดจะใช้เส้นทางที่เร่งด้วยฮาร์ดแวร์ใน Windows, ChromeOS และ Chrome สำหรับ Android ใน Mac และ Linux เฉพาะหน้าที่ต้องมีการคอมโพสิตเนื้อหาบางส่วนเท่านั้นที่จะใช้เส้นทางที่เร่งความเร็ว (ดูข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่ต้องมีการคอมโพสิตได้ที่ด้านล่าง) แต่อีกไม่นานทุกหน้าเว็บจะใช้เส้นทางที่เร่งความเร็วด้วย
สุดท้าย เราจะเจาะลึกการทำงานของเครื่องมือแสดงผลและดูฟีเจอร์ที่มีผลอย่างมากต่อประสิทธิภาพ เมื่อพยายามปรับปรุงประสิทธิภาพของเว็บไซต์ของคุณเอง การทําความเข้าใจรูปแบบเลเยอร์อาจมีประโยชน์ แต่ก็ทําให้เสียเปรียบได้ง่ายๆ เนื่องจากเลเยอร์เป็นโครงสร้างที่มีประโยชน์ แต่การสร้างเลเยอร์จํานวนมากอาจทําให้เกิดความล่าช้าในกองกราฟิก โปรดรับทราบข้อมูลนี้
จาก DOM ไปยังหน้าจอ
ขอแนะนําเลเยอร์
เมื่อโหลดและแยกวิเคราะห์หน้าเว็บแล้ว ระบบจะแสดงหน้าเว็บในเบราว์เซอร์เป็นโครงสร้างที่นักพัฒนาเว็บจํานวนมากคุ้นเคย ซึ่งก็คือ DOM อย่างไรก็ตาม เมื่อแสดงผลหน้าเว็บ เบราว์เซอร์จะมีชุดการแสดงผลขั้นกลางที่ไม่ได้แสดงต่อนักพัฒนาซอฟต์แวร์โดยตรง โครงสร้างที่สำคัญที่สุดคือเลเยอร์
ใน Chrome เลเยอร์มีหลายประเภทด้วยกัน ได้แก่ เลเยอร์การแสดงผล (RenderLayers) ซึ่งรับผิดชอบสำหรับซับต้นไม้ของ DOM และเลเยอร์กราฟิก (GraphicsLayers) ซึ่งรับผิดชอบสำหรับซับต้นไม้ของเลเยอร์การแสดงผล ตัวเลือกหลังนี้น่าสนใจที่สุดสำหรับเรา เนื่องจาก GraphicsLayers คือสิ่งที่จะอัปโหลดไปยัง GPU เป็นพื้นผิว จากนี้ไปเราจะใช้คำว่า "เลเยอร์" เพื่อหมายถึง GraphicsLayer
เกร็ดความรู้สั้นๆ เกี่ยวกับคำศัพท์ของ GPU: พื้นผิวคืออะไร ลองนึกถึงภาพบิตแมปที่ย้ายจากหน่วยความจำหลัก (RAM) ไปยังหน่วยความจำวิดีโอ (VRAM ใน GPU) เมื่ออยู่ใน GPU แล้ว คุณจะแมปกับเรขาคณิตของเมชได้ ในวิดีโอเกมหรือโปรแกรม CAD เทคนิคนี้ใช้เพื่อทำให้โมเดล 3 มิติโครงกระดูกมี "ผิวหนัง" Chrome ใช้พื้นผิวเพื่อนำข้อมูลบางส่วนของเนื้อหาหน้าเว็บไปยัง GPU คุณสามารถแมปพื้นผิวไปยังตำแหน่งต่างๆ และการเปลี่ยนรูปแบบได้อย่างง่ายดายโดยใช้พื้นผิวกับเมชสี่เหลี่ยมผืนผ้าที่เรียบง่าย นี่คือวิธีการทำงานของ CSS 3 มิติ และเหมาะอย่างยิ่งสำหรับการเลื่อนอย่างรวดเร็ว เราจะอธิบายเพิ่มเติมเกี่ยวกับทั้ง 2 อย่างนี้ในภายหลัง
มาดูตัวอย่าง 2-3 ตัวอย่างเพื่ออธิบายแนวคิดเลเยอร์กัน
เครื่องมือที่มีประโยชน์มากเมื่อศึกษาเลเยอร์ใน Chrome คือ Flag "แสดงเส้นขอบเลเยอร์แบบคอมโพสิต" ในการตั้งค่า (ไอคอนรูปเฟืองเล็กๆ) ในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ ในส่วนหัว "การแสดงผล" ซึ่งจะไฮไลต์ตำแหน่งของเลเยอร์บนหน้าจอ มาเปิดกันเลย ภาพหน้าจอและตัวอย่างเหล่านี้ทั้งหมดนำมาจาก Chrome Canary เวอร์ชันล่าสุด ซึ่งเป็น Chrome 27 ขณะเขียนบทความนี้
รูปที่ 1: หน้าเว็บแบบเลเยอร์เดียว
<!doctype html>
<html>
<body>
<div>I am a strange root.</div>
</body>
</html>

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

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

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