Ringkasan arsitektur RenderingNG

Chris Harrelson
Chris Harrelson

Dalam postingan sebelumnya, saya memberikan ringkasan tentang sasaran arsitektur dan properti utama RenderingNG. Postingan ini akan menjelaskan cara bagian komponennya disiapkan, dan alur pipeline rendering melaluinya.

Dimulai dari level tertinggi dan melihat perincian dari sana, tugas rendering adalah:

  1. Render konten menjadi piksel di layar.
  2. Menganimasikan efek visual pada konten dari satu status ke status lainnya.
  3. Scroll sebagai respons terhadap input.
  4. Merutekan input secara efisien ke tempat yang tepat sehingga skrip developer dan subsistem lainnya dapat merespons.

Konten yang akan dirender adalah hierarki bingkai untuk setiap tab browser, ditambah UI browser. Dan, aliran peristiwa input mentah dari layar sentuh, mouse, keyboard, dan perangkat hardware lainnya.

Setiap frame berisi:

  • Status DOM
  • CSS
  • Kanvas
  • Resource eksternal, seperti gambar, video, font, dan SVG

Bingkai adalah dokumen HTML, beserta URL-nya. Halaman web yang dimuat di tab browser memiliki frame level teratas, frame turunan untuk setiap iframe yang disertakan dalam dokumen level atas, dan turunan iframe rekursifnya.

Efek visual adalah operasi grafis yang diterapkan pada bitmap, seperti scroll, transformasi, klip, filter, opasitas, atau gabungan.

Komponen arsitektur

Dalam RenderingNG, tugas-tugas ini dibagi secara logis ke beberapa tahap dan komponen kode. Komponen tersebut berakhir di berbagai proses CPU, thread, dan sub-komponen di dalam thread tersebut. Masing-masing memainkan peran penting dalam mencapai keandalan, performa skalabel, dan perluasan untuk semua konten web.

Merender struktur pipeline

Diagram pipeline rendering seperti yang dijelaskan dalam teks berikut.

Rendering berlangsung di pipeline dengan sejumlah tahapan dan artefak yang dibuat di sepanjang proses. Setiap tahap mewakili kode yang melakukan satu tugas yang didefinisikan dengan baik dalam rendering. Artefak adalah struktur data yang merupakan input atau output dari tahapan; dalam input atau output diagram ditunjukkan dengan panah.

Postingan blog ini tidak akan membahas terlalu detail tentang artefak; yang akan dibahas dalam postingan berikutnya: Key Data Structures dan perannya dalam RenderingNG.

Tahapan pipeline

Dalam diagram sebelumnya, tahapan dinotasikan dengan warna yang menunjukkan thread atau proses mana yang dijalankan:

  • Hijau: thread utama proses render
  • Kuning: Pengomposisi proses render
  • Oranye: proses viz

Dalam beberapa kasus, string dapat dieksekusi di beberapa tempat, bergantung pada keadaannya, karena itulah beberapa memiliki dua warna.

Tahapan tersebut adalah:

  1. Animasikan: mengubah gaya terkomputasi dan memutasikan hierarki properti dari waktu ke waktu berdasarkan linimasa deklaratif.
  2. Gaya: menerapkan CSS ke DOM, dan membuat gaya terkomputasi.
  3. Tata letak: menentukan ukuran dan posisi elemen DOM di layar, dan membuat hierarki fragmen yang tidak dapat diubah.
  4. Pre-paint: menghitung hierarki properti dan invalidate daftar tampilan dan kartu tekstur GPU yang sesuai.
  5. Scroll: memperbarui offset scroll dokumen dan elemen DOM yang dapat di-scroll, dengan mengubah hierarki properti.
  6. Paint: menghitung daftar tampilan yang menjelaskan cara melakukan raster ubin tekstur GPU dari DOM.
  7. Commit: menyalin hierarki properti dan daftar tampilan ke thread compositor.
  8. Layerize: membagi daftar tampilan menjadi daftar lapisan gabungan untuk rasterisasi dan animasi independen.
  9. Worklet raster, dekode, dan paint: mengubah daftar tampilan, gambar yang dienkode, dan kode worklet paint menjadi kartu tekstur GPU.
  10. Aktifkan: membuat bingkai compositor yang mewakili cara menggambar dan memosisikan ubin GPU ke layar, bersama dengan efek visual apa pun.
  11. Agregat: menggabungkan frame compositor dari semua frame compositor yang terlihat ke dalam frame compositor global tunggal.
  12. Gambar: jalankan frame compositor yang digabungkan di GPU untuk membuat piksel di layar.

Tahapan pipeline rendering dapat dilewati jika tidak diperlukan. Misalnya, animasi efek visual, serta scroll, dapat melewati tata letak, pra-penggambaran, dan menggambar. Inilah sebabnya animasi dan scroll ditandai dengan titik-titik kuning dan hijau dalam diagram. Jika tata letak, pra-penggambaran, dan menggambar dapat dilewati untuk efek visual, semua itu dapat dijalankan sepenuhnya di thread compositor dan melewati thread utama.

Rendering UI browser tidak digambarkan langsung di sini, tetapi dapat dianggap sebagai versi sederhana dari pipeline yang sama ini (dan pada kenyataannya, implementasinya berbagi banyak kode). Video (juga tidak digambarkan secara langsung) umumnya dirender melalui kode independen yang mendekode frame ke ubin tekstur GPU yang kemudian dicolokkan ke frame compositor dan langkah menggambar.

Proses dan struktur thread

Proses CPU

Penggunaan beberapa proses CPU mampu menghasilkan isolasi keamanan dan performa antara situs dan dari status browser, serta isolasi keamanan dan stabilitas dari hardware GPU.

Diagram berbagai bagian dari proses CPU

  • Proses render merender, menganimasikan, men-scroll, dan merutekan input untuk kombinasi situs dan tab tunggal. Ada banyak proses render.
  • Proses browser merender, menganimasikan, dan merutekan input untuk UI browser (termasuk kolom URL, judul tab dan ikon), serta merutekan semua input yang tersisa ke proses render yang sesuai. Hanya ada satu proses browser.
  • Proses Viz menggabungkan komposisi dari beberapa proses render plus proses browser. Fungsi tersebut akan melakukan raster dan menggambar menggunakan GPU. Hanya ada satu proses {i>Viz<i}.

Situs yang berbeda selalu berakhir dalam proses render yang berbeda. (Kenyataannya, selalu berada di desktop; jika memungkinkan di perangkat seluler. Saya akan menulis "{i>always<i}" di bawah ini, tetapi peringatan ini berlaku secara keseluruhan.)

Beberapa tab atau jendela browser di situs yang sama biasanya masuk dalam proses render yang berbeda, kecuali jika tab terkait (satu membuka proses yang lain). Dalam tekanan memori yang kuat pada desktop, Chromium dapat menempatkan beberapa tab dari situs yang sama ke dalam proses render yang sama meskipun tidak berkaitan.

Dalam satu tab browser, bingkai dari situs yang berbeda selalu berada dalam proses render yang berbeda satu sama lain, tetapi bingkai dari situs yang sama selalu berada dalam proses render yang sama. Dari perspektif rendering, manfaat penting dari beberapa proses render adalah bahwa iframe dan tab lintas situs mencapai isolasi performa dari satu sama lain. Selain itu, origin dapat ikut serta dalam isolasi yang lebih luas.

Hanya ada satu proses Viz untuk seluruh Chromium. Lagi pula, biasanya hanya ada satu GPU dan layar untuk menggambar. Memisahkan Viz ke dalam prosesnya sendiri akan memberikan stabilitas dalam menghadapi bug di driver GPU atau hardware. Ini juga bagus untuk isolasi keamanan, yang penting untuk API GPU seperti Vulkan. Ini juga penting untuk keamanan secara umum.

Karena browser dapat memiliki banyak tab dan jendela, dan semuanya memiliki piksel UI browser untuk digambar, Anda mungkin bertanya-tanya: mengapa hanya ada satu proses browser? Alasannya adalah hanya salah satunya yang difokuskan pada satu waktu; faktanya, tab browser yang tidak terlihat sebagian besar dinonaktifkan dan kehilangan semua memori GPU-nya. Namun, fitur rendering UI browser yang kompleks semakin sering diterapkan dalam proses render (dikenal sebagai WebUI). Ini bukan untuk alasan isolasi performa, tetapi untuk memanfaatkan kemudahan penggunaan mesin rendering web Chromium.

Di perangkat Android lama, proses render dan browser dibagikan saat digunakan di WebView (ini tidak berlaku untuk Chromium di Android secara umum, hanya WebView). Di WebView, proses browser juga dibagikan ke aplikasi penyematan, dan WebView hanya memiliki satu proses render.

Terkadang juga ada proses utilitas untuk mendekode konten video yang dilindungi. Proses ini tidak digambarkan di atas.

Rangkaian percakapan

Thread membantu mencapai isolasi performa dan responsivitas meskipun tugas berjalan lambat, paralelisasi pipeline, dan beberapa buffering.

Diagram proses render seperti yang dijelaskan dalam artikel.

  • Thread utama menjalankan skrip, loop peristiwa rendering, siklus proses dokumen, hit test, pengiriman peristiwa skrip, dan penguraian HTML, CSS, dan format data lainnya.
    • Helper thread utama melakukan tugas-tugas seperti membuat bitmap dan blob gambar yang memerlukan encoding atau decoding.
    • Web Worker menjalankan skrip, dan loop peristiwa rendering untuk OffscreenCanvas.
  • Thread Compositor memproses peristiwa input, melakukan scroll dan animasi konten web, menghitung lapisanisasi konten web yang optimal, dan mengoordinasikan dekode gambar, worklet paint, dan tugas raster.
    • Helper thread compiler mengoordinasikan tugas raster Viz, dan menjalankan tugas dekode gambar, worklet paint, dan raster penggantian.
  • Thread media, demuxer, atau output audio mendekode, memproses, dan menyinkronkan streaming video dan audio. (Ingat bahwa video dijalankan secara paralel dengan pipeline rendering utama.)

Memisahkan thread utama dan compositor sangat penting untuk isolasi performa animasi dan scroll dari tugas thread utama.

Hanya ada satu thread utama per proses render, meskipun beberapa tab atau frame dari situs yang sama mungkin berakhir dalam proses yang sama. Namun, ada isolasi performa dari pekerjaan yang dilakukan di berbagai API browser. Misalnya, pembuatan bitmap dan blob gambar di Canvas API berjalan di thread helper thread utama.

Demikian juga, hanya ada satu thread compositor per proses render. Umumnya bukan masalah bahwa hanya ada satu, karena semua operasi yang sangat mahal di thread compositor didelegasikan ke thread pekerja compositor atau proses Viz, dan pekerjaan ini dapat dilakukan secara paralel dengan perutean input, scroll, atau animasi. Thread pekerja compiler mengoordinasikan tugas yang berjalan dalam proses Viz, tetapi akselerasi GPU di mana saja dapat gagal karena alasan di luar kontrol Chromium, seperti bug driver. Dalam situasi ini thread pekerja akan melakukan pekerjaan dalam mode penggantian pada CPU.

Jumlah thread pekerja compositor bergantung pada kemampuan perangkat. Misalnya, desktop umumnya akan menggunakan lebih banyak thread karena memiliki lebih banyak core CPU dan lebih hemat daya daripada perangkat seluler. Ini adalah contoh meningkatkan dan menurunkan skala.

Menarik juga untuk dicatat bahwa arsitektur threading proses render adalah penerapan dari tiga pola pengoptimalan yang berbeda:

  • Thread helper: mengirimkan subtugas yang berjalan lama ke thread tambahan, untuk menjaga thread induk tetap responsif terhadap permintaan lain yang terjadi secara bersamaan. Thread helper thread utama dan thread helper compositor adalah contoh bagus dari teknik ini.
  • Multi-buffer: menampilkan konten yang sebelumnya dirender saat merender konten baru, untuk menyembunyikan latensi rendering. Thread compositor menggunakan teknik ini.
  • Paralelisasi pipeline: menjalankan pipeline rendering di beberapa tempat secara bersamaan. Ini adalah cara untuk men-scroll dan animasi menjadi cepat, meskipun update rendering thread utama sedang berlangsung, karena scroll dan animasi dapat berjalan secara paralel.

Proses browser

Diagram proses browser yang menunjukkan hubungan antara thread Render dan pengomposisian, serta helper thread pengomposisian dan render.

  • Thread render dan komposisi merespons input dalam UI browser, mengarahkan input lain ke proses render yang benar; menyusun tata letak dan menggambar UI browser.
  • Helper thread merender dan pengomposisian menjalankan tugas dekode gambar dan raster atau dekode penggantian gambar.

Thread proses browser dan pengomposisian serupa dengan kode dan fungsi proses render, kecuali thread utama dan thread compositor yang digabungkan menjadi satu. Dalam kasus ini, hanya ada satu thread yang diperlukan karena isolasi performa dari tugas thread utama yang panjang tidak diperlukan, karena tidak ada thread yang sengaja dibuat.

Proses Viz

Diagram yang menunjukkan bahwa proses Viz mencakup thread utama GPU, dan thread compositor tampilan.

  • Thread utama GPU raster menampilkan daftar dan frame video menjadi ubin tekstur GPU, dan menggambar frame compositor ke layar.
  • Thread compositor tampilan menggabungkan dan mengoptimalkan pengomposisian dari setiap proses render, serta proses browser, ke dalam satu frame compositor untuk ditampilkan di layar.

Raster dan menggambar umumnya terjadi di thread yang sama, karena keduanya bergantung pada resource GPU, dan sulit untuk secara andal menggunakan multi-thread menggunakan GPU (akses multi-thread yang lebih mudah ke GPU adalah salah satu motivasi untuk mengembangkan standar Vulkan baru). Di Android WebView, ada thread render tingkat OS yang terpisah untuk menggambar karena cara WebView disematkan ke aplikasi native. Platform lain mungkin akan memiliki thread tersebut di masa mendatang.

Kompositor layar berada di thread yang berbeda karena harus selalu responsif, dan tidak memblokir kemungkinan sumber pelambatan di thread utama GPU. Salah satu penyebab pelambatan pada thread utama GPU adalah panggilan ke kode non-Chromium, seperti driver GPU khusus vendor, yang mungkin lambat dengan cara yang sulit diprediksi.

Struktur komponen

Dalam setiap thread utama atau compositor proses render, ada komponen software logis yang berinteraksi satu sama lain secara terstruktur.

Komponen thread utama proses rendering

Diagram perender Blink.

  • Perender Blink:
    • Fragmen hierarki frame lokal mewakili hierarki frame lokal dan DOM dalam frame.
    • Komponen DOM dan Canvas API berisi implementasi dari semua API ini.
    • Runner siklus proses dokumen menjalankan langkah-langkah pipeline rendering hingga dan termasuk langkah commit.
    • Komponen pengujian hit peristiwa input dan pengiriman menjalankan hit test untuk mengetahui elemen DOM mana yang ditarget oleh suatu peristiwa, dan menjalankan algoritma pengiriman peristiwa input dan perilaku default.
  • Penjadwal dan runner loop peristiwa rendering memutuskan apa yang akan dijalankan di loop peristiwa dan waktunya. Class ini menjadwalkan rendering agar terjadi pada ritme yang cocok dengan layar perangkat.

Diagram pohon frame.

Fragmen hierarki frame lokal agak rumit untuk dipikirkan. Ingat kembali bahwa hierarki frame adalah halaman utama dan iframe turunannya, secara rekursif. Frame bersifat lokal untuk proses render jika dirender dalam proses tersebut, dan jika tidak, dilakukan jarak jauh.

Anda dapat membayangkan mewarnai bingkai sesuai dengan proses render mereka. Pada gambar sebelumnya, lingkaran hijau adalah semua frame dalam satu proses render; yang oranye ada dalam proses kedua, dan yang biru ada di proses ketiga.

Fragmen hierarki frame lokal adalah komponen yang terhubung dengan warna yang sama dalam hierarki frame. Ada empat pohon {i>frame tree<i} lokal dalam gambar: dua untuk situs A, satu untuk situs B, dan satu untuk situs C. Setiap pohon frame lokal mendapatkan komponen perender Blink-nya sendiri. Perender Blink hierarki frame lokal mungkin berada dalam proses render yang sama dengan hierarki frame lokal lainnya (ditentukan oleh cara proses render dipilih, seperti yang dijelaskan sebelumnya).

Struktur thread compositor proses render

Diagram yang menunjukkan komponen compositor proses render.

Komponen compositor proses render meliputi:

  • Pengendali data yang mengelola daftar lapisan gabungan, daftar tampilan, dan hierarki properti.
  • Runner siklus proses yang menjalankan langkah animasi, scroll, komposit, raster, dan mendekode, serta mengaktifkan langkah-langkah pipeline rendering. (Ingat bahwa animasi dan scroll dapat terjadi di thread utama dan compositor.)
  • Pengendali input dan hit test melakukan pemrosesan input dan hit test pada resolusi lapisan yang digabungkan, untuk menentukan apakah gestur scroll dapat dijalankan pada thread compositor, dan hit test proses render mana yang harus ditargetkan.

Contoh dalam praktik

Sekarang mari kita buat arsitekturnya secara konkret dengan sebuah contoh. Dalam contoh ini, ada tiga tab:

Tab 1: foo.com

<html>
  <iframe id=one src="foo.com/other-url"></iframe>
  <iframe  id=two src="bar.com"></iframe>
</html>

Tab 2: bar.com

<html>
 …
</html>

Tab 3: baz.com html <html> … </html>

Proses, thread, dan struktur komponen untuk tab ini akan terlihat seperti ini:

Diagram proses untuk tab.

Sekarang, mari kita lihat satu contoh dari empat tugas utama rendering, yang mungkin Anda ingat adalah:

  1. Render konten menjadi piksel di layar.
  2. Menganimasikan efek visual pada konten dari satu status ke status lainnya.
  3. Scroll sebagai respons terhadap input.
  4. Rutekan input secara efisien ke tempat yang tepat sehingga skrip developer dan subsistem lainnya dapat merespons.

Untuk merender DOM yang diubah untuk tab satu:

  1. Skrip developer mengubah DOM dalam proses render untuk foo.com.
  2. Perender Blink memberi tahu compositor bahwa render diperlukan.
  3. Compositor memberi tahu Viz bahwa render diperlukan.
  4. Viz memberi sinyal awal render kembali ke compositor.
  5. Kompositor meneruskan sinyal awal ke perender Blink.
  6. Runner loop peristiwa thread utama menjalankan siklus proses dokumen.
  7. Thread utama mengirimkan hasilnya ke thread compositor.
  8. Runner loop peristiwa compositor menjalankan siklus proses komposisi.
  9. Setiap tugas raster dikirim ke Viz untuk raster (sering kali ada lebih dari satu tugas ini).
  10. Viz raster konten pada GPU.
  11. Viz mengonfirmasi penyelesaian tugas raster. Catatan: Chromium sering kali tidak menunggu raster selesai, tetapi menggunakan sesuatu yang disebut token sinkronisasi yang harus diselesaikan oleh tugas raster sebelum langkah 15 dijalankan.
  12. Frame compositor dikirim ke Viz.
  13. Viz menggabungkan frame compositor untuk proses render foo.com, proses render iframe bar.com, dan UI browser.
  14. Viz menjadwalkan seri.
  15. Viz menggambar frame compositor gabungan ke layar.

Untuk menganimasikan transisi transformasi CSS pada tab dua:

  1. Thread compositor untuk proses render bar.com menandai animasi dalam loop peristiwa compositor-nya dengan mengubah hierarki properti yang ada. Tindakan ini kemudian akan menjalankan kembali siklus proses compositor. (Tugas raster dan dekode dapat terjadi, tetapi tidak digambarkan di sini.)
  2. Frame compositor dikirim ke Viz.
  3. Viz menggabungkan frame compositor untuk proses render foo.com, proses render bar.com, dan UI browser.
  4. Viz menjadwalkan seri.
  5. Viz menggambar frame compositor gabungan ke layar.

Untuk men-scroll halaman web di tab tiga:

  1. Urutan peristiwa input (mouse, sentuh, atau keyboard) masuk ke proses browser.
  2. Setiap peristiwa dirutekan ke thread compositor proses render baz.com.
  3. Compositor menentukan apakah thread utama perlu mengetahui peristiwa tersebut.
  4. Peristiwa akan dikirim, jika perlu, ke thread utama.
  5. Thread utama mengaktifkan pemroses peristiwa input (pointerdown, touchstar, pointermove, touchmove, atau wheel) untuk melihat apakah pemroses akan memanggil preventDefault pada peristiwa.
  6. Thread utama akan menampilkan apakah preventDefault dipanggil ke compositor.
  7. Jika tidak, peristiwa input akan dikirim kembali ke proses browser.
  8. Proses browser mengonversinya menjadi gestur scroll dengan menggabungkannya dengan peristiwa terbaru lainnya.
  9. Gestur scroll dikirim sekali lagi ke thread compositor proses render baz.com,
  10. Scroll diterapkan di sana, dan thread compositor untuk proses render bar.com akan menandai animasi dalam loop peristiwa compositor-nya. Tindakan ini kemudian akan mengubah offset scroll di hierarki properti dan menjalankan kembali siklus proses compositor. Kode ini juga memberi tahu thread utama untuk mengaktifkan peristiwa scroll (tidak digambarkan di sini).
  11. Frame compositor dikirim ke Viz.
  12. Viz menggabungkan frame compositor untuk proses render foo.com, proses render bar.com, dan UI browser.
  13. Viz menjadwalkan seri.
  14. Viz menggambar frame compositor gabungan ke layar.

Untuk merutekan peristiwa click pada hyperlink di iframe #two di tab satu:

  1. Peristiwa input (mouse, sentuh, atau keyboard) masuk ke proses browser. Compose melakukan perkiraan hit test untuk menentukan bahwa proses render iframe bar.com harus menerima klik, dan mengirimkannya ke sana.
  2. Thread compositor untuk bar.com merutekan peristiwa click ke thread utama untuk bar.com dan menjadwalkan tugas loop peristiwa rendering untuk memprosesnya.
  3. Pemroses peristiwa input untuk pengujian hit thread utama bar.com guna menentukan elemen DOM mana dalam iframe yang diklik, lalu mengaktifkan peristiwa click untuk diamati oleh skrip. Tidak mendengar preventDefault, fitur akan membuka hyperlink.
  4. Setelah memuat halaman tujuan hyperlink, status baru dirender, dengan langkah-langkah yang mirip dengan contoh "render DOM yang diubah" di atas. (Perubahan berikutnya tidak digambarkan di sini.)

Kesimpulan

Fiuh, detail sekali. Seperti yang Anda lihat, proses rendering di Chromium cukup rumit. Butuh banyak waktu untuk mengingat dan menginternalisasi semua bagian, jadi jangan khawatir jika tampak berlebihan.

Poin utama yang paling penting adalah bahwa ada pipeline rendering yang sederhana secara konseptual, yang melalui modularisasi cermat dan perhatian terhadap detail, telah dibagi menjadi sejumlah komponen mandiri. Komponen ini kemudian telah dibagi menjadi beberapa proses paralel dan thread untuk memaksimalkan peluang performa skalabel dan perluasan.

Masing-masing komponen tersebut berperan penting dalam memungkinkan semua performa dan fitur yang dibutuhkan aplikasi web modern. Kami akan segera memublikasikan penjelasan mendalam tentang masing-masing peran tersebut, dan peran penting yang mereka mainkan.

Namun sebelum itu, saya juga akan menjelaskan pentingnya struktur data utama yang disebutkan dalam postingan ini (yang ditunjukkan dengan warna biru di sisi diagram pipeline rendering) sama pentingnya dalam RenderingNG dengan komponen kode.

Terima kasih telah membaca, dan nantikan kabar terbarunya!

Ilustrasi oleh Una Kravets.