Model lapisan
Pengantar
Bagi sebagian besar developer web, model dasar halaman web adalah DOM. Rendering adalah proses yang sering kali tidak jelas untuk mengubah representasi halaman ini menjadi gambar di layar. Browser modern telah mengubah cara kerja rendering dalam beberapa tahun terakhir untuk memanfaatkan kartu grafis: hal ini sering kali disebut secara samar sebagai “akselerasi hardware”. Saat berbicara tentang halaman web normal (yaitu bukan Canvas2D atau WebGL), apa sebenarnya arti istilah tersebut? Artikel ini menjelaskan model dasar yang mendukung rendering konten web yang dipercepat hardware di Chrome.
Peringatan Besar dan Tebal
Kita sedang membahas WebKit di sini, dan lebih khusus lagi, kita sedang membahas port Chromium dari WebKit. Artikel ini membahas detail penerapan Chrome, bukan fitur platform web. Platform dan standar web tidak mengkodifikasi tingkat detail implementasi ini, sehingga tidak ada jaminan bahwa apa pun dalam artikel ini akan berlaku untuk browser lain, tetapi pengetahuan tentang internal tetap dapat berguna untuk proses debug lanjutan dan penyesuaian performa.
Selain itu, perhatikan bahwa seluruh artikel ini membahas bagian inti dari arsitektur rendering Chrome yang berubah sangat cepat. Artikel ini mencoba membahas hanya hal-hal yang tidak mungkin berubah, tetapi tidak ada jaminan bahwa semuanya akan tetap berlaku dalam enam bulan.
Penting untuk memahami bahwa Chrome telah memiliki dua jalur rendering yang berbeda selama beberapa waktu: jalur yang diakselerasi hardware dan jalur software yang lebih lama. Saat ini, semua halaman menggunakan jalur yang diakselerasi hardware di Windows, ChromeOS, dan Chrome untuk Android. Di Mac dan Linux, hanya halaman yang memerlukan komposisi untuk sebagian kontennya yang menggunakan jalur yang dipercepat (lihat di bawah untuk mengetahui lebih lanjut hal-hal yang memerlukan komposisi), tetapi dalam waktu dekat semua halaman juga akan menggunakan jalur yang dipercepat di sana.
Terakhir, kita akan melihat bagian dalam mesin rendering dan melihat fitur-fiturnya yang berdampak besar pada performa. Saat mencoba meningkatkan performa situs Anda sendiri, sebaiknya pahami model lapisan, tetapi Anda juga dapat melakukan kesalahan: lapisan adalah konstruksi yang berguna, tetapi membuat banyak lapisan dapat menyebabkan overhead di seluruh stack grafis. Harap pertimbangkan hal ini.
Dari DOM ke Layar
Memperkenalkan Lapisan
Setelah dimuat dan diuraikan, halaman akan ditampilkan di browser sebagai struktur yang dikenal oleh banyak developer web: DOM. Namun, saat merender halaman, browser memiliki serangkaian representasi perantara yang tidak ditampilkan langsung kepada developer. Yang paling penting dari struktur ini adalah lapisan.
Di Chrome, sebenarnya ada beberapa jenis lapisan: RenderLayers, yang bertanggung jawab atas sub-pohon DOM, dan GraphicsLayers, yang bertanggung jawab atas sub-pohon RenderLayers. Yang terakhir ini paling menarik bagi kita di sini, karena GraphicsLayers adalah yang diupload ke GPU sebagai tekstur. Saya akan menggunakan istilah “lapisan” untuk GraphicsLayer.
Sekilas tentang terminologi GPU: apa itu tekstur? Anggaplah sebagai gambar bitmap yang dipindahkan dari memori utama (yaitu RAM) ke memori video (yaitu VRAM, di GPU Anda). Setelah berada di GPU, Anda dapat memetakan tekstur ke geometri mesh. Dalam game video atau program CAD, teknik ini digunakan untuk memberi model 3D kerangka “kulit”. Chrome menggunakan tekstur untuk mendapatkan potongan konten halaman web ke GPU. Tekstur dapat dipetakan dengan mudah ke berbagai posisi dan transformasi dengan menerapkannya ke mesh persegi panjang yang sangat sederhana. Ini adalah cara kerja CSS 3D, dan juga sangat cocok untuk scroll cepat -- tetapi kita akan membahas keduanya nanti.
Mari kita lihat beberapa contoh untuk menggambarkan konsep lapisan.
Alat yang sangat berguna saat mempelajari lapisan di Chrome adalah flag “tampilkan batas lapisan gabungan” di setelan (yaitu ikon roda gigi kecil) di Alat Developer, di bagian judul “rendering”. Sorotan ini dengan mudah menandai posisi lapisan di layar. Mari kita aktifkan. Screenshot dan contoh ini semuanya diambil dari Chrome Canary terbaru, Chrome 27 pada saat penulisan ini.
Gambar 1: Halaman satu lapisan
<!doctype html>
<html>
<body>
<div>I am a strange root.</div>
</body>
</html>

Halaman ini hanya memiliki satu lapisan. Petak biru mewakili ubin, yang dapat Anda anggap sebagai sub-unit lapisan yang digunakan Chrome untuk mengupload bagian dari lapisan besar sekaligus ke GPU. Hal ini tidak terlalu penting di sini.
Gambar 2: Elemen dalam lapisannya sendiri
<!doctype html>
<html>
<body>
<div style="transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
I am a strange root.
</div>
</body>
</html>

Dengan menempatkan properti CSS 3D pada <div>
yang memutarnya, kita dapat melihat tampilannya saat elemen mendapatkan lapisannya sendiri: perhatikan batas oranye, yang menguraikan lapisan dalam tampilan ini.
Kriteria Pembuatan Lapisan
Apa lagi yang memiliki lapisannya sendiri? Heuristik Chrome di sini telah berkembang dari waktu ke waktu dan akan terus berkembang, tetapi saat ini salah satu dari tindakan berikut akan memicu pembuatan lapisan:
- Properti CSS transformasi 3D atau perspektif
- Elemen
<video>
yang menggunakan decoding video yang dipercepat - Elemen
<canvas>
dengan konteks 3D (WebGL) atau konteks 2D yang dipercepat - Plugin gabungan (yaitu Flash)
- Elemen dengan animasi CSS untuk opasitas atau menggunakan transformasi animasi
- Elemen dengan filter CSS yang dipercepat
- Elemen memiliki turunan yang memiliki lapisan komposisi (dengan kata lain, jika elemen memiliki elemen turunan yang berada di lapisannya sendiri)
- Elemen memiliki saudara dengan indeks z yang lebih rendah yang memiliki lapisan gabungan (dengan kata lain, elemen dirender di atas lapisan gabungan)
Implikasi Praktis: Animasi
Kita juga dapat memindahkan lapisan, yang membuatnya sangat berguna untuk animasi.
Gambar 3: Lapisan Animasi
<!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>
Seperti yang disebutkan sebelumnya, lapisan sangat berguna untuk memindahkan konten web statis. Dalam kasus dasar, Chrome melukis konten lapisan ke dalam bitmap software sebelum menguploadnya ke GPU sebagai tekstur. Jika konten tersebut tidak berubah pada masa mendatang, konten tersebut tidak perlu dicat ulang. Hal ini merupakan Hal yang Baik: proses pengecatan ulang memerlukan waktu yang dapat digunakan untuk hal lain, seperti menjalankan JavaScript, dan jika proses pengecatan ulang berlangsung lama, hal ini akan menyebabkan gangguan atau penundaan dalam animasi.
Lihat, misalnya, tampilan linimasa Dev Tools ini: tidak ada operasi gambar saat lapisan ini berputar bolak-balik.

Tidak valid! Mengecat ulang
Namun, jika konten lapisan berubah, lapisan tersebut harus dicat ulang.
Gambar 4: Lapisan Pengecatan Ulang
<!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>
Setiap kali elemen input diklik, elemen yang berputar akan bertambah lebar 1 piksel. Hal ini menyebabkan penataan ulang dan pengecatan ulang seluruh elemen, yang dalam hal ini adalah seluruh lapisan.
Cara yang baik untuk melihat apa yang dicat adalah dengan alat “tampilkan persegi panjang cat” di Alat Pengembangan, juga di bagian judul “Rendering” pada setelan Alat Pengembangan. Setelah diaktifkan, perhatikan bahwa elemen animasi dan tombol berkedip merah saat tombol diklik.

Peristiwa gambar juga muncul di linimasa Dev Tools. Pembaca yang jeli mungkin melihat ada dua peristiwa gambar di sana: satu untuk lapisan, dan satu untuk tombol itu sendiri, yang akan dicat ulang saat berubah ke/dari status ditekan.

Perhatikan bahwa Chrome tidak selalu perlu mengecat ulang seluruh lapisan, tetapi mencoba pintar dengan hanya mengecat ulang bagian DOM yang telah dibatalkan validasinya. Dalam hal ini, elemen DOM yang kita ubah adalah ukuran seluruh lapisan. Namun, dalam banyak kasus lainnya, akan ada banyak elemen DOM dalam satu lapisan.
Pertanyaan berikutnya yang jelas adalah apa yang menyebabkan pembatalan validasi dan memaksa proses repaint. Pertanyaan ini sulit dijawab secara menyeluruh karena ada banyak kasus ekstrem yang dapat memaksa pembatalan validasi. Penyebab paling umum adalah mengotori DOM dengan memanipulasi gaya CSS atau menyebabkan tata letak ulang. Tony Gentilcore memiliki postingan blog yang bagus tentang penyebab tata letak ulang, dan Stoyan Stefanov memiliki artikel yang membahas proses menggambar secara lebih mendetail (tetapi hanya membahas proses menggambar, bukan hal-hal pengomposisian yang canggih).
Cara terbaik untuk mengetahui apakah hal ini memengaruhi sesuatu yang sedang Anda kerjakan adalah dengan menggunakan alat Linimasa Dev Tools dan Tampilkan Rects Cat untuk melihat apakah Anda mengecat ulang saat tidak ingin melakukannya, lalu coba identifikasi tempat Anda mengotori DOM tepat sebelum tata letak ulang/pengecatan ulang tersebut. Jika proses menggambar tidak dapat dihindari, tetapi tampaknya memerlukan waktu yang tidak wajar, lihat artikel Eberhard Gräther tentang mode menggambar berkelanjutan di Alat Developer.
Menyatukan semuanya: DOM ke Layar
Jadi, bagaimana cara Chrome mengubah DOM menjadi gambar layar? Secara konseptual, fitur ini:
- Mengambil DOM dan membaginya menjadi beberapa lapisan
- Menggambar setiap lapisan ini secara independen ke dalam bitmap software
- Menguploadnya ke GPU sebagai tekstur
- Menggabungkan berbagai lapisan menjadi satu dalam gambar layar akhir.
Semuanya harus terjadi saat pertama kali Chrome menghasilkan frame halaman web. Namun, Anda dapat menggunakan beberapa pintasan untuk frame berikutnya:
- Jika properti CSS tertentu berubah, Anda tidak perlu mengecat ulang apa pun. Chrome hanya dapat merekomposisi lapisan yang ada yang sudah berada di GPU sebagai tekstur, tetapi dengan properti komposisi yang berbeda (misalnya, dalam posisi yang berbeda, dengan opasitas yang berbeda, dll.).
- Jika sebagian lapisan menjadi tidak valid, lapisan tersebut akan dicat ulang dan diupload ulang. Jika kontennya tetap sama, tetapi atribut gabungannya berubah (misalnya, diterjemahkan atau opasitas berubah), Chrome dapat membiarkannya di GPU dan merekomposisi untuk membuat frame baru.
Seperti yang sekarang sudah jelas, model komposisi berbasis lapisan memiliki implikasi mendalam untuk performa rendering. Komposisi relatif murah jika tidak ada yang perlu dicat, sehingga menghindari pengecatan ulang lapisan adalah sasaran keseluruhan yang baik saat mencoba men-debug performa rendering. Developer yang cerdas akan melihat daftar pemicu komposisi di atas dan menyadari bahwa pembuatan lapisan dapat dipaksa dengan mudah. Namun, berhati-hatilah saat membuat instance secara membabi buta, karena instance tidak gratis: instance menggunakan memori di RAM sistem dan di GPU (terutama terbatas pada perangkat seluler) dan memiliki banyak instance dapat menyebabkan overhead lain dalam logika yang melacak instance yang terlihat. Banyak lapisan juga dapat meningkatkan waktu yang dihabiskan untuk merasterisasi jika lapisan tersebut besar dan banyak tumpang-tindih di tempat yang sebelumnya tidak tumpang-tindih, sehingga menyebabkan apa yang terkadang disebut sebagai “overdraw”. Jadi, gunakan pengetahuan Anda dengan bijak.
Sekian untuk saat ini. Nantikan beberapa artikel lainnya tentang implikasi praktis model lapisan.