Rendering Dipercepat di Chrome

Model lapisan

Tom Wiltzius
Tom Wiltzius

Pengantar

Bagi sebagian besar 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. Browser modern telah mengubah cara kerja rendering dalam beberapa tahun terakhir untuk memanfaatkan kartu grafis: hal ini sering disebut samar-samar sebagai “akselerasi perangkat keras”. Saat berbicara tentang halaman web normal (yaitu, bukan Canvas2D atau WebGL), apa arti istilah itu sebenarnya? Artikel ini menjelaskan model dasar yang melandasi rendering konten web dengan akselerasi hardware di Chrome.

Peringatan Besar dan Lemak

Kita berbicara tentang WebKit di sini, dan lebih khusus lagi tentang porta Chromium WebKit. Artikel ini membahas detail implementasi Chrome, bukan fitur platform web. Platform web dan standar tidak mengodifikasi tingkat detail implementasi ini, sehingga tidak ada jaminan 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, perlu diketahui bahwa seluruh artikel ini membahas bagian inti dari arsitektur rendering Chrome yang berubah dengan sangat cepat. Artikel ini hanya mencoba membahas hal-hal yang tidak mungkin berubah, tetapi tidak ada jaminan bahwa semuanya akan tetap berlaku dalam waktu enam bulan.

Penting untuk dipahami bahwa Chrome telah memiliki dua jalur rendering yang berbeda untuk sementara waktu: jalur akselerasi hardware dan jalur software lama. Saat penulisan ini, semua halaman akan masuk ke jalur akselerasi hardware di Windows, ChromeOS, dan Chrome untuk Android. Di Mac dan Linux, hanya halaman yang memerlukan pengomposisian untuk beberapa kontennya yang akan melalui jalur yang dipercepat (lihat di bawah untuk informasi lebih lanjut tentang apa yang memerlukan pengomposisian), tetapi dalam waktu dekat semua halaman juga akan masuk ke jalur yang dipercepat.

Terakhir, kami mengintip ke balik layar mesin rendering dan melihat fitur-fiturnya yang berdampak besar pada performa. Saat mencoba meningkatkan kinerja situs Anda sendiri, akan sangat membantu jika memahami model lapisan, tetapi juga mudah untuk menggambarkan diri Anda sendiri: {i>layer<i} adalah konstruksi yang berguna, tetapi membuat banyak lapisan dapat menimbulkan overhead di seluruh tumpukan grafis. Ingatlah bahwa Anda telah diperingatkan!

Dari DOM ke Layar

Memperkenalkan Lapisan

Setelah halaman dimuat dan diuraikan, halaman tersebut akan ditampilkan di browser sebagai struktur yang dikenal oleh 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 lapisan.

Di Chrome sebenarnya ada beberapa jenis lapisan: RenderLayers, yang bertanggung jawab atas subpohon DOM, dan GraphicsLayers, yang bertanggung jawab atas subpohon RenderLayers. Yang terakhir ini paling menarik bagi kita di sini, karena GraphicsLayers adalah apa yang diupload ke GPU sebagai tekstur. Saya hanya akan mengatakan "{i>layer<i}" yang berarti {i>GraphicsLayer<i}.

Mengesampingkan 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 ada 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 memindahkan potongan konten laman web ke GPU. Tekstur dapat dipetakan ke berbagai posisi dan transformasi secara murah dengan menerapkannya ke mesh persegi panjang yang sangat sederhana. Beginilah cara kerja 3D CSS, dan juga 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 bendera “tampilkan batas lapisan gabungan” di setelan (yaitu ikon roda gigi kecil) pada Dev Tools, di bawah judul “rendering”. Ini sangat sederhana menyoroti di mana lapisan berada di layar. Mari kita aktifkan. Semua screenshot dan contoh ini 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>
Screenshot batas render lapisan gabungan di sekitar lapisan dasar halaman
Screenshot batas render lapisan gabungan di sekitar lapisan dasar halaman

Halaman ini hanya memiliki satu lapisan. Petak biru mewakili ubin, yang dapat Anda anggap sebagai sub-unit dari lapisan yang digunakan Chrome untuk mengupload bagian dari lapisan besar pada satu waktu ke GPU. Mereka tidak terlalu penting di sini.

Gambar 2: Elemen di 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 dirotasi

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 terus berlanjut, tetapi saat ini, salah satu pembuatan lapisan pemicu berikut adalah:

  • Properti CSS transformasi 3D atau perspektif
  • Elemen <video> menggunakan decoding video yang dipercepat
  • Elemen <canvas> dengan konteks 3D (WebGL) atau konteks 2D yang dipercepat
  • Plugin gabungan (misalnya Flash)
  • Elemen dengan animasi CSS untuk opasitasnya atau yang menggunakan transformasi animasi
  • Elemen dengan filter CSS yang dipercepat
  • Elemen memiliki turunan yang memiliki lapisan pengomposisi (dengan kata lain jika elemen tersebut memiliki elemen turunan yang berada di lapisannya sendiri)
  • Elemen memiliki seinduk dengan indeks z yang lebih rendah yang memiliki lapisan pengomposisian (dengan kata lain, dirender di atas lapisan gabungan)

Implikasi Praktis: Animasi

Kita juga dapat memindahkan {i>layer<i}, 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 disebutkan sebelumnya, {i>layer<i} sangat berguna untuk berpindah-pindah di sekitar konten web statis. Dalam kasus dasar, Chrome akan melukis konten lapisan ke dalam bitmap software sebelum menguploadnya ke GPU sebagai tekstur. Jika konten tersebut tidak berubah di masa mendatang, konten tersebut tidak perlu diperbarui. Ini adalah Hal yang Bagus: menggambar ulang membutuhkan waktu yang bisa dihabiskan untuk hal-hal lain, seperti menjalankan JavaScript, dan jika cat itu panjang, hal itu akan menyebabkan hambatan atau penundaan dalam animasi.

Sebagai contoh, lihat tampilan linimasa Dev Tools: tidak ada operasi cat saat lapisan ini berputar bolak-balik.

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

Tidak valid! Pengecatan ulang

Namun, jika konten lapisan berubah, lapisan tersebut harus dicat 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 akan menjadi lebih lebar 1 piksel. Ini menyebabkan penataan ulang dan penggambaran ulang seluruh elemen, yang dalam hal ini adalah seluruh lapisan.

Cara yang baik untuk melihat apa yang dilukis adalah dengan alat "{i>show paint rects<i}" di Dev Tools, juga di bawah judul "Rendering" dari pengaturan Dev Tools. Setelah mengaktifkannya, perhatikan bahwa elemen animasi dan tombol keduanya berkedip merah saat tombol diklik.

Screenshot kotak centang show paint rects
Screenshot kotak centang show rects

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

Screenshot Dev Tools Linimasa yang melukis ulang lapisan
Screenshot Linimasa Dev Tools yang melukis ulang lapisan

Perhatikan bahwa Chrome tidak selalu perlu melukis ulang seluruh lapisan, tetapi Chrome mencoba secara cerdas dengan hanya mengecat ulang bagian DOM yang telah dibatalkan validasinya. Dalam hal ini, elemen DOM yang kita modifikasi adalah ukuran seluruh lapisan. Namun di banyak kasus lainnya, akan ada banyak elemen DOM dalam satu layer.

Pertanyaan yang jelas berikutnya adalah apa yang menyebabkan pembatalan validasi dan memaksa pengujian ulang. Ini sulit untuk dijawab secara menyeluruh karena ada banyak kasus ekstrem yang dapat memaksa pembatalan. Penyebab yang paling umum adalah mengotori DOM dengan memanipulasi gaya CSS atau menyebabkan penataan ulang. Tony Gentilcore memiliki postingan blog yang bagus tentang penyebab penataan ulang, dan Stoyan Stefanov memiliki artikel yang membahas melukis secara lebih mendetail (tetapi berakhir dengan hanya melukis, bukan pengomposisian yang mewah ini).

Cara terbaik untuk mengetahui apakah hal itu memengaruhi sesuatu yang sedang Anda kerjakan adalah dengan menggunakan alat Dev Tools Linimasa dan Show Paint Rectangle untuk melihat apakah Anda melakukan pengecatan ulang saat menginginkannya, lalu coba identifikasi di mana Anda mengotori DOM tepat sebelum menata ulang/menggambar ulang. Jika proses melukis tidak dapat dihindari, tetapi tampaknya memakan waktu yang terlalu lama, lihat artikel Eberhard Gräther tentang mode pengecatan berkelanjutan di Dev Tools.

Menggabungkan semuanya: DOM ke Layar

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

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

Semua itu perlu dilakukan saat Chrome pertama kali menghasilkan bingkai halaman web. Namun, diperlukan beberapa pintasan untuk frame mendatang:

  1. Jika properti CSS tertentu berubah, tidak perlu mengecat ulang apa pun. Chrome hanya dapat mengomposisi ulang lapisan yang ada yang sudah berada 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 kontennya tetap sama tetapi atribut gabungannya berubah (misalnya, diterjemahkan atau opasitasnya berubah), Chrome dapat membiarkannya di GPU dan menggantinya untuk membuat bingkai baru.

Sudah jelas sekarang, model pengomposisian berbasis lapisan memiliki implikasi mendalam untuk kinerja rendering. Komposisi relatif murah jika tidak ada yang perlu digambar, jadi menghindari penggambaran ulang lapisan adalah sasaran keseluruhan yang baik saat mencoba men-debug performa rendering. Pengembang yang cerdas akan melihat daftar pemicu pengomposisian di atas dan menyadari bahwa memaksa pembuatan lapisan dengan mudah bisa dilakukan. Tetapi berhati-hatilah saat membuatnya begitu saja, karena tidak gratis: shader menghabiskan memori di RAM sistem dan di GPU (khususnya terbatas pada perangkat seluler) dan memiliki banyak elemen tersebut dapat menimbulkan overhead lain dalam logika yang melacaknya. Banyak lapisan juga sebenarnya dapat meningkatkan waktu yang dihabiskan untuk merasterisasi jika lapisannya besar dan banyak tumpang tindih di tempat yang sebelumnya tidak, sehingga menyebabkan apa yang kadang-kadang disebut sebagai “overdraw”. Jadi, gunakan pengetahuan Anda dengan bijak!

Sekian dari kami. Nantikan beberapa artikel lainnya tentang implikasi praktis dari model lapisan.

Referensi Tambahan