Rendering Dipercepat di Chrome

Model lapisan

Tom Wiltzius
Tom Wiltzius

Pengantar

Bagi kebanyakan developer web, model dasar halaman web adalah DOM. Rendering adalah proses yang sering kali tidak jelas dalam mengubah representasi halaman ini menjadi gambar di layar. Dalam beberapa tahun terakhir, browser modern telah mengubah cara kerja rendering untuk memanfaatkan kartu grafis: hal ini sering kali samar-samar disebut sebagai "akselerasi hardware". Ketika berbicara tentang halaman web normal (yaitu bukan Canvas2D atau WebGL), apa sebenarnya arti istilah itu? Artikel ini menjelaskan model dasar yang mendukung rendering konten web dengan akselerasi hardware di Chrome.

Peringatan Besar dan Berlemak

Kita berbicara tentang WebKit, dan lebih spesifik lagi, kita membahas port Chromium WebKit. Artikel ini membahas detail implementasi Chrome, bukan fitur platform web. Platform web dan standar tidak mengodifikasi level detail implementasi ini, sehingga tidak ada jaminan bahwa apa pun dalam artikel ini akan diterapkan ke browser lain, tetapi pengetahuan tentang internal tetap berguna untuk proses debug lanjutan dan penyesuaian performa.

Perlu diperhatikan juga bahwa seluruh artikel ini membahas bagian inti dari arsitektur rendering Chrome yang berubah dengan sangat cepat. Artikel ini hanya membahas hal-hal yang tidak mungkin berubah, tetapi tidak ada jaminan bahwa semua itu akan tetap berlaku dalam waktu enam bulan.

Penting untuk dipahami bahwa Chrome memiliki dua jalur rendering yang berbeda untuk sementara waktu: jalur dengan akselerasi hardware dan jalur software lama. Saat tulisan ini dibuat, semua halaman mengarah ke jalur akselerasi hardware di Windows, ChromeOS, dan Chrome untuk Android. Di Mac dan Linux, hanya halaman yang perlu pengomposisian untuk sebagian konten mereka yang berada di jalur yang dipercepat (lihat di bawah untuk info selengkapnya tentang apa yang memerlukan pengomposisian), tetapi dengan cepat semua halaman juga akan melalui jalur yang dipercepat.

Terakhir, kita mengintip ke dalam mesin rendering dan melihat fitur-fiturnya yang memiliki dampak besar pada performa. Saat mencoba meningkatkan performa situs Anda sendiri, akan sangat membantu jika Anda memahami model lapisan, tetapi juga mudah untuk menentukan sendiri: lapisan adalah konstruksi yang berguna, tetapi membuat banyak lapisan dapat menimbulkan overhead di seluruh tumpukan grafis. Anggap diri Anda sudah diperingatkan!

Dari DOM ke Layar

Memperkenalkan Lapisan

Setelah halaman dimuat dan diuraikan, halaman ditampilkan dalam browser sebagai struktur yang dikenal banyak developer web: DOM. Namun, saat merender halaman, browser memiliki serangkaian representasi perantara yang tidak langsung diekspos kepada developer. Yang paling penting dari struktur-struktur ini adalah {i>layer<i}.

Di Chrome sebenarnya ada beberapa jenis lapisan: RenderLayers, yang bertanggung jawab atas sub-hierarki DOM, dan GraphicsLayers, yang bertanggung jawab atas sub-hierarki dari RenderLayers. Yang kedua ini paling menarik bagi kita di sini, karena GraphicsLayers adalah apa yang diupload ke GPU sebagai tekstur. Mulai sekarang saya akan mengatakan "layer" berarti GraphicsLayer.

Kesampingkan sekilas pada terminologi GPU: apa itu tekstur? Anggap saja sebagai gambar bitmap yang dipindahkan dari memori utama (yaitu RAM) ke memori video (yaitu VRAM, di GPU Anda). Setelah berada di GPU, Anda dapat memetakannya ke geometri mesh -- dalam video game atau program CAD, teknik ini digunakan untuk memberikan "kulit" pada model 3D kerangka. Chrome menggunakan tekstur untuk memasukkan potongan konten halaman web ke GPU. Tekstur dapat dipetakan dengan murah ke berbagai posisi dan transformasi dengan menerapkannya ke mesh persegi panjang yang sangat sederhana. Begitulah cara kerja CSS 3D, dan bagus untuk scrolling dengan 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 tanda "tampilkan batas lapisan gabungan" dalam setelan (misalnya ikon roda gigi kecil) di Dev Tools, di bawah judul "rendering". Fitur ini sangat sederhana menyoroti tempat lapisan berada di layar. Mari kita aktifkan. Semua screenshot dan contoh ini diambil dari Chrome Canary terbaru, Chrome 27 pada saat panduan ini ditulis.

Gambar 1: Halaman satu lapis

<!doctype html>
<html>
<body>
  <div>I am a strange root.</div>
</body>
</html>
Screenshot lapisan gabungan yang merender batas di sekitar lapisan dasar halaman
Screenshot dari batas render lapisan yang digabungkan di sekeliling lapisan dasar halaman

Halaman ini hanya memiliki satu lapisan. Kisi biru mewakili ubin, yang bisa Anda anggap sebagai sub-unit lapisan yang digunakan Chrome untuk mengunggah bagian lapisan besar sekaligus ke GPU. Mereka 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>
Screenshot batas render lapisan yang diputar
Screenshot batas render lapisan yang diputar

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 mendapatkan lapisannya sendiri? Heuristik Chrome di sini telah berkembang dari waktu ke waktu dan akan terus berlanjut, tetapi saat ini merupakan salah satu pembuatan lapisan pemicu berikut:

  • Properti CSS transformasi 3D atau perspektif
  • <video> elemen 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 opasitasnya atau yang 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 kerabat dengan indeks z lebih rendah yang memiliki lapisan komposisi (dengan kata lain elemen tersebut dirender di atas lapisan gabungan)

Implikasi Praktis: Animasi

Kita juga bisa memindahkan {i>layer<i}, sehingga 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 melukiskan konten lapisan ke dalam bitmap software sebelum menguploadnya ke GPU sebagai tekstur. Jika konten tersebut tidak berubah di masa mendatang, konten tersebut tidak perlu digambar ulang. Bagus: menggambar ulang membutuhkan waktu yang dapat dihabiskan untuk hal-hal lain, seperti menjalankan JavaScript, dan jika paint-nya panjang, maka akan menyebabkan halangan atau penundaan dalam animasi.

Misalnya, lihat tampilan linimasa Dev Tools ini: tidak ada operasi paint saat lapisan ini berputar bolak-balik.

Screenshot linimasa Dev Tools selama animasi
Screenshot linimasa Dev Tools selama animasi

Tidak valid! Pengecatan ulang

Tetapi jika konten lapisan berubah, lapisan harus digambar ulang.

Gambar 4: Melukis Ulang Lapisan

<!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 menjadi lebih lebar 1px. Hal ini menyebabkan penataan ulang dan penggambaran ulang seluruh elemen, yang dalam hal ini merupakan keseluruhan lapisan.

Cara yang baik untuk melihat apa yang digambar adalah dengan menggunakan alat “show paint rects” di Dev Tools, yang juga ada di bawah judul “Rendering” di setelan Dev Tools. Setelah mengaktifkannya, perhatikan bahwa elemen animasi dan tombol berkedip merah saat tombol diklik.

Screenshot kotak centang show paint rects
Screenshot kotak centang show paint rects

Peristiwa paint juga muncul di {i>timeline<i} Dev Tools. Pembaca yang berpikiran tajam mungkin memperhatikan ada dua peristiwa paint di sana: satu untuk lapisan, dan satu untuk tombol itu sendiri, yang di-paint ulang ketika berubah ke/dari keadaan tertekan.

Screenshot Dev Tools Timeline yang melukis ulang lapisan
Screenshot Dev Tools Timeline yang melukis ulang sebuah lapisan

Perhatikan bahwa Chrome tidak selalu perlu menggambar ulang seluruh lapisan, tetapi Chrome mencoba secara cerdas hanya menggambar ulang bagian DOM yang telah dibatalkan validasinya. Dalam hal ini, elemen DOM yang kita ubah adalah ukuran seluruh lapisan. Akan tetapi di banyak kasus lain, akan ada banyak elemen DOM dalam sebuah layer.

Pertanyaan berikutnya yang jelas adalah apa yang menyebabkan pembatalan validasi dan memaksa penulisan ulang. Hal ini sulit untuk 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 penataan ulang tata letak. Tony Gentilcore memiliki postingan blog yang bagus tentang penyebab tata letak ulang, dan Stoyan Stefanov memiliki artikel yang membahas tentang melukis secara lebih detail (tetapi diakhiri hanya dengan melukis, bukan omposisi yang rumit ini).

Cara terbaik untuk mengetahui apakah itu memengaruhi sesuatu yang sedang Anda kerjakan adalah dengan menggunakan alat Dev Tools Timeline dan Show Paint Rects untuk melihat apakah Anda sedang mengecat ulang jika tidak menginginkannya, lalu cobalah untuk mengidentifikasi tempat Anda mengotori DOM tepat sebelum menata ulang/menggambar ulang. Jika proses menggambar tidak bisa dihindari tetapi tampaknya memakan waktu yang sangat lama, lihat artikel Eberhard Gräther tentang mode melukis berkelanjutan di Dev Tools.

Menyatukan: DOM ke Layar

Jadi, bagaimana cara Chrome mengubah DOM menjadi gambar layar? Secara konseptual, ini:

  1. Mengambil DOM dan membaginya menjadi beberapa lapisan
  2. Mewarnai setiap lapisan ini secara independen ke dalam bitmap software
  3. Menguploadnya ke GPU sebagai tekstur
  4. Gabungkan berbagai lapisan menjadi satu gambar layar akhir.

Semua itu harus terjadi saat Chrome pertama kali membuat frame halaman web. Namun, diperlukan beberapa pintasan untuk frame mendatang:

  1. Jika properti CSS tertentu berubah, Anda tidak perlu melakukan penggambaran ulang apa pun. Chrome hanya dapat menggabungkan lapisan yang ada yang sudah ada di GPU sebagai tekstur, tetapi dengan properti komposisi yang berbeda (mis. pada posisi yang berbeda, dengan opasitas yang berbeda, dll).
  2. Jika sebagian lapisan menjadi tidak valid, lapisan tersebut akan digambar ulang dan diupload ulang. Jika konten tetap sama, tetapi atribut gabungannya berubah (misalnya diterjemahkan atau opasitasnya berubah), Chrome dapat membiarkannya di GPU dan menyatu ulang untuk membuat bingkai baru.

Seperti yang seharusnya sudah jelas, model pengomposisian berbasis lapisan memiliki implikasi mendalam terhadap performa rendering. Pengomposisian relatif murah ketika tidak ada yang perlu digambar, jadi menghindari pengecatan ulang lapisan adalah tujuan keseluruhan yang baik saat mencoba men-debug performa rendering. Pengembang yang cerdas akan melihat daftar pemicu komposisi di atas dan menyadari bahwa mudah untuk memaksakan pembuatan lapisan. Namun, berhati-hatilah jika membuatnya secara membabi buta, karena jenisnya tidak gratis: model ini menggunakan memori di RAM sistem dan GPU (terutama terbatas di perangkat seluler) dan memiliki banyak di antaranya dapat menyebabkan overhead lain dalam logika yang melacak mana yang terlihat. Banyak lapisan juga dapat meningkatkan waktu yang dihabiskan untuk meraster jika lapisan-lapisan itu besar dan banyak tumpang tindih dari tempat yang tidak sebelumnya, yang menyebabkan apa yang terkadang disebut sebagai “kelebihan”. Jadi, gunakan pengetahuan Anda dengan bijak!

Sekian untuk saat ini. Nantikan beberapa artikel lainnya tentang implikasi praktis dari model lapisan.

Referensi Tambahan