Arsitektur RenderingNG

Chris Harrelson
Chris Harrelson

Di sini Anda akan menemukan bagaimana komponen RenderingNG disiapkan, dan bagaimana pipeline rendering mengalir melaluinya.

Mulai dari level tertinggi, tugas rendering adalah:

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

Konten yang akan dirender adalah pohon bingkai untuk setiap tab browser, ditambah dengan di antarmuka browser web. Dan, aliran peristiwa input mentah dari layar sentuh, {i>mouse<i}, {i>keyboard<i}, dan perangkat keras lainnya.

Setiap frame mencakup:

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

Frame adalah dokumen HTML beserta URL-nya. Laman web yang dimuat di tab {i>browser<i} memiliki {i>frame<i} tingkat atas, bingkai turunan untuk setiap iframe yang disertakan dalam dokumen tingkat 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 ini dibagi secara logis dalam beberapa tahap dan kode komponen. Komponen berakhir di berbagai proses CPU, thread, dan sub-komponen di dalam thread tersebut. Masing-masing keterampilan berperan penting dalam mencapai keandalan, performa skalabel dan ekstensibilitas untuk semua konten web.

Struktur pipeline rendering

Diagram pipeline rendering.
Panah menunjukkan input dan output setiap tahap. Tahap tidak ditandai dengan warna, untuk menunjukkan thread atau proses mana yang dieksekusi. Di beberapa dalam beberapa kasus, stage dapat dieksekusi di beberapa tempat, tergantung pada keadaan, itulah sebabnya beberapa memiliki dua warna. Green stage thread utama proses render; kuning adalah compositor proses {i>render<i}; tahap oranye adalah proses {i>viz<i}.

Rendering dilanjutkan di pipeline dengan sejumlah tahap dan artefak yang dibuat dalam perjalanan ini. Setiap tahap merepresentasikan kode yang melakukan satu tugas yang didefinisikan dengan baik di dalam proses rendering. Artefak adalah struktur data yang merupakan input atau {i>output<i} dari tahapan-tahapan.

Tahapannya adalah:

  1. Animasikan: mengubah gaya komputasi dan memutasikan hierarki properti dari waktu ke waktu berdasarkan linimasa deklaratif.
  2. Gaya: menerapkan CSS ke DOM, dan membuat gaya komputasi.
  3. Tata letak: menentukan ukuran dan posisi elemen DOM di layar, dan membuat hierarki fragmen yang tidak dapat diubah.
  4. Pre-Paint: komputasi hierarki properti dan batalkan semua daftar tampilan dan kartu tekstur GPU yang ada.
  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. Membuat lapisan: membagi daftar tampilan menjadi daftar lapisan gabungan untuk proses raster dan animasi independen.
  9. Worklet raster, dekode, dan cat: mengubah daftar tampilan, gambar yang dienkode, dan kode worklet cat, secara berurutan, menjadi Kartu tekstur GPU.
  10. Aktifkan: buat frame compositor yang mewakili cara menggambar dan memosisikan kartu GPU ke layar, bersama dengan efek visual apa pun.
  11. Agregat: menggabungkan frame compositor dari semua frame compositor yang terlihat menjadi satu frame compositor global.
  12. Draw: mengeksekusi frame compositor gabungan pada GPU untuk membuat piksel di layar.

Tahapan pipeline rendering dapat dilewati jika tidak diperlukan. Misalnya, animasi efek visual, dan scroll, bisa melewati tata letak, pra-lukis, dan menggambar. Inilah sebabnya animasi dan scroll ditandai dengan titik kuning dan hijau pada diagram. Jika {i>layout<i}, {i>pre-Paint<i}, dan {i>cat<i} dapat dilewati untuk efek visual, bisa dijalankan sepenuhnya di thread compositor dan melewati thread utama.

Rendering UI browser tidak digambarkan secara langsung di sini, tetapi dapat dianggap sebagai versi sederhana dari pipeline yang sama (dan sebenarnya implementasinya menggunakan banyak kode yang sama). Video (juga tidak digambarkan secara langsung) umumnya merender dengan kode independen yang mendekode frame menjadi petak tekstur GPU yang kemudian dicolokkan ke {i> frame<i} compositor dan langkah menggambar.

Struktur proses dan thread

Proses CPU

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

Diagram berbagai bagian proses CPU

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

Situs yang berbeda selalu berakhir dalam berbagai proses render.

Beberapa tab atau jendela browser dari situs yang sama biasanya masuk dalam render yang berbeda proses, kecuali jika tab terkait, seperti satu membuka lainnya. Di bawah tekanan memori yang kuat pada desktop, Chromium dapat menempatkan beberapa tab dari situs yang sama ke dalam proses render yang sama meskipun tidak terkait.

Dalam satu tab {i>browser<i}, {i>frame<i} dari situs yang berbeda selalu berada dalam proses {i>render<i} yang berbeda satu sama lain, tetapi {i>frame<i} dari situs yang sama selalu dalam proses {i>render<i} yang sama. Dari perspektif rendering, keuntungan penting dari beberapa proses render adalah bahwa iframe lintas situs dan tab mencapai isolasi performa satu sama lain. Selain itu, origin dapat memilih untuk melakukan isolasi lebih lanjut.

Hanya ada satu proses Viz untuk seluruh Chromium, karena biasanya hanya satu GPU dan layar untuk menggambar.

Memisahkan {i>Viz<i} ke dalam prosesnya sendiri baik untuk stabilitas dalam menghadapi {i>bug<i} di Driver atau hardware GPU. Alat ini juga bagus untuk isolasi keamanan, yang penting untuk API GPU seperti Vulkan dan keamanan secara umum.

Karena browser dapat memiliki banyak tab dan jendela, dan semuanya memiliki piksel {i> browser<i} UI untuk digambar, Anda mungkin bertanya-tanya: mengapa hanya ada satu proses browser? Alasannya adalah hanya satu dari mereka yang fokus pada satu waktu; pada kenyataannya, tab browser yang tidak terlihat sebagian besar dinonaktifkan dan meninggalkan semua memori GPU mereka. Namun, fitur rendering UI browser yang kompleks makin sering diterapkan dalam proses render (dikenal sebagai WebUI). Ini bukan karena alasan pengisolasian 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 terdapat proses utilitas untuk mendekode konten video yang dilindungi. Proses ini tidak digambarkan dalam diagram sebelumnya.

Thread

Thread membantu mencapai isolasi dan responsivitas performa meskipun tugasnya lambat, paralelisasi pipeline dan banyak buffering.

Diagram proses render.

  • Thread utama menjalankan skrip, loop peristiwa rendering, siklus proses dokumen, hit testing, pengiriman peristiwa skrip, dan penguraian HTML, CSS, serta format data lainnya.
    • Helper thread utama melakukan tugas seperti membuat bitmap dan blob gambar yang memerlukan encoding atau decoding.
    • Pekerja Web menjalankan skrip, dan loop kejadian rendering untuk OffscreenCanvas.
  • Thread Compositor memproses peristiwa input, melakukan pengguliran dan animasi pada konten web, menghitung lapisan konten web yang optimal, dan mengoordinasikan dekode gambar, worklet cat, dan tugas raster.
    • Pembantu thread kompositor mengoordinasikan tugas raster Viz, dan menjalankan tugas dekode gambar, worklet cat, dan raster fallback.
  • dekode media, demuxer, atau output audio, memproses dan menyinkronkan aliran video dan audio. (Ingat bahwa video dijalankan secara paralel dengan pipeline rendering utama.)

Memisahkan utas utama dan {i>compositor<i} sangat penting untuk isolasi performa animasi dan aktivitas scroll dari thread utama.

Hanya ada satu thread utama per proses render, meskipun beberapa tab atau {i>frame<i} dari situs yang sama mungkin berakhir dalam proses yang sama. Namun, terdapat isolasi performa dari pekerjaan yang dilakukan di berbagai API browser. Misalnya, pembuatan bitmap dan blob gambar di Canvas API berjalan di thread bantuan 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 Compositor mengoordinasikan tugas yang berjalan dalam proses {i>Viz<i}, tetapi akselerasi GPU di mana-mana dapat gagal karena alasan di luar kendali Chromium, seperti bug driver. Dalam situasi ini, thread pekerja akan melakukan pekerjaan dalam mode fallback pada CPU.

Jumlah thread pekerja compositor bergantung pada kemampuan perangkat. Misalnya, {i>desktop<i} umumnya akan menggunakan lebih banyak {i>thread<i}, karena memiliki lebih banyak inti CPU dan lebih murah daripada perangkat seluler. Ini adalah contoh meningkatkan dan memperkecil skala.

Arsitektur threading proses render adalah penerapan tiga jenis pola pengoptimalan:

  • Thread bantuan: mengirimkan subtugas yang berjalan lama ke thread tambahan untuk mempertahankan thread induk responsif terhadap permintaan simultan lainnya. Thread utama thread helper dan compositor helper adalah contoh yang baik dari teknik ini.
  • Beberapa buffering: menampilkan konten yang dirender sebelumnya saat merender konten baru, untuk menyembunyikan terhadap latensi rendering. Thread compositor menggunakan teknik ini.
  • Paralelisasi pipeline: menjalankan pipeline rendering di beberapa tempat secara bersamaan. Beginilah cara scroll dan animasi bisa cepat; bahkan jika sedang terjadi pembaruan rendering thread utama, scroll dan animasi dapat dijalankan secara paralel.

Proses browser

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

  • Thread render dan pengomposisian merespons input di UI browser, mengarahkan input lain ke proses render yang benar; mengatur tata letak dan melukiskan UI browser.
  • Penunjang thread render dan pengomposisian mengeksekusi tugas dekode gambar dan fallback raster atau dekode.

Thread yang dirender dan pengomposisian proses browser serupa pada kode dan fungsionalitas proses {i>render<i}, kecuali bahwa thread utama dan thread kompositor digabungkan menjadi satu. Hanya ada satu utas yang diperlukan dalam kasus ini karena tidak perlu isolasi performa dari tugas thread utama yang panjang, karena desainnya tidak ada.

Proses Viz

Proses Viz mencakup thread utama GPU, dan thread compositor tampilan.

  • Raster Thread utama GPU menampilkan daftar dan frame video ke dalam ubin tekstur GPU, dan menggambar {i>frame<i} compositor ke layar.
  • Thread compositor display menggabungkan dan mengoptimalkan komposisi dari setiap proses render, ditambah proses {i>browser<i}, menjadi satu {i>frame<i} compositor untuk presentasi ke layar.

Raster dan gambar umumnya terjadi pada thread yang sama, karena keduanya mengandalkan resource GPU, dan sulit untuk menggunakan GPU multi-thread (akses multi-thread yang lebih mudah ke GPU adalah salah satu motivasi kami untuk mengembangkan Vulkan). Pada Android WebView, ada thread render tingkat OS yang terpisah untuk menggambar karena cara WebView disematkan ke aplikasi native. Platform lain kemungkinan akan memiliki thread seperti ini pada masa mendatang.

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

Struktur komponen

Dalam setiap thread utama atau compositor proses render, terdapat komponen perangkat lunak logis yang berinteraksi satu sama lain secara terstruktur.

Komponen thread utama proses render

Diagram perender Blink.

Di 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 hit testing dan pengiriman peristiwa input menjalankan hit test untuk mencari tahu elemen DOM mana yang ditargetkan oleh sebuah peristiwa, dan menjalankan peristiwa input pengiriman algoritma dan perilaku {i>default<i}.

Penjadwal dan runner loop peristiwa rendering menentukan apa yang akan dijalankan pada peristiwa berulang dan kapan. Fungsi ini menjadwalkan rendering agar terjadi pada ritme yang cocok dengan perangkat tampilan.

Diagram hierarki frame.

Fragmen hierarki frame lokal sedikit rumit. Mengingat kembali bahwa hierarki frame adalah halaman utama dan iframe turunannya, secara berulang. Frame bersifat lokal untuk proses render jika dirender dalam proses tersebut, dan sebaliknya, {i>remote<i}.

Anda bisa membayangkan mewarnai {i>frame<i} sesuai dengan proses rendernya. Dalam gambar sebelumnya, lingkaran hijau adalah semua bingkai dalam satu proses render; yang oranye di satu detik, dan yang biru di sepertiga.

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

Struktur thread kompositor proses render

Diagram yang menunjukkan komponen compositor proses render.

Komponen kompositor proses render meliputi:

  • Pengendali data yang mengelola daftar lapisan gabungan, daftar tampilan, dan hierarki properti.
  • Runner siklus proses yang menjalankan animasi, scroll, komposit, raster, serta mendekode dan 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 testing pada resolusi lapisan yang digabungkan, untuk menentukan apakah gestur scroll dapat dijalankan di thread compositor, dan hit test proses render mana yang harus ditargetkan.

Contoh arsitektur dalam praktik

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>

Struktur proses, thread, dan komponen untuk tab ini terlihat seperti berikut:

Diagram proses untuk tab.

Mari kita pelajari satu contoh masing-masing dari empat tugas utama rendering. Pengingat:

  1. Render konten menjadi piksel di layar.
  2. Menganimasikan efek visual pada konten dari satu status ke status lainnya.
  3. Scroll untuk merespons 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 perlu dilakukan.
  3. Kompositor memberi tahu Viz bahwa diperlukan render agar terjadi.
  4. Viz menandakan dimulainya {i>render<i} kembali ke compositor.
  5. Kompositor meneruskan sinyal awal ke perender Berkedip.
  6. Runner loop peristiwa thread utama menjalankan siklus proses dokumen.
  7. Thread utama mengirimkan hasil ke thread compositor.
  8. Runner loop peristiwa compositor menjalankan siklus proses pengomposisian.
  9. Tugas raster apa pun dikirim ke Viz untuk raster (sering kali ada lebih dari satu tugas ini).
  10. Viz mengubah konten di GPU.
  11. Viz mengakui penyelesaian tugas raster. Catatan: Chromium sering kali tidak menunggu hingga raster selesai, dan sebaliknya menggunakan sesuatu yang disebut sebagai token sinkronisasi yang harus diselesaikan oleh tugas raster sebelum langkah 15 dieksekusi.
  12. {i>Frame compositor<i} dikirim ke Viz.
  13. {i>Viz<i} menggabungkan {i>frame<i} compositor untuk proses render foo.com, proses render iframe bar.com, dan UI browser.
  14. Viz menjadwalkan undian.
  15. Viz menggambar {i>frame<i} compositor teragregasi ke layar.

Untuk menganimasikan transisi transformasi CSS di tab dua:

  1. Thread compositor untuk proses render bar.com menandai animasi di loop peristiwa compositor-nya dengan mengubah hierarki properti yang ada. Ini kemudian menjalankan kembali siklus hidup compositor. (Tugas raster dan dekode dapat terjadi, tetapi tidak digambarkan di sini.)
  2. {i>Frame compositor<i} dikirim ke Viz.
  3. Viz menggabungkan bingkai compositor untuk proses render foo.com, proses render bar.com, dan UI browser.
  4. Viz menjadwalkan undian.
  5. Viz menggambar {i>frame<i} compositor teragregasi ke layar.

Untuk men-scroll halaman web di tab tiga:

  1. Urutan peristiwa input (mouse, sentuh, atau keyboard) muncul pada proses browser.
  2. Setiap peristiwa dirutekan ke thread compositor proses render baz.com.
  3. Compositor menentukan apakah thread utama perlu mengetahui tentang peristiwa tersebut.
  4. Peristiwa ini 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 tersebut.
  6. Thread utama 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 utas compositor proses render baz.com,
  10. Scroll diterapkan di sana, dan utas compositor untuk bar.com proses render menandai animasi dalam loop kejadian compositor-nya. Tindakan ini kemudian mengubah offset scroll di hierarki properti dan menjalankan ulang siklus proses compositor. Kode ini juga memberi tahu thread utama untuk mengaktifkan peristiwa scroll (tidak digambarkan di sini).
  11. {i>Frame compositor<i} dikirim ke Viz.
  12. {i>Viz<i} menggabungkan {i>frame<i} compositor untuk proses render foo.com, proses {i>render<i} bar.com, dan UI {i>browser<i}.
  13. Viz menjadwalkan undian.
  14. Viz menggambar {i>frame<i} compositor teragregasi ke layar.

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

  1. Peristiwa input (mouse, sentuh, atau keyboard) muncul pada proses browser. Menjalankan perkiraan hit test untuk menentukan bahwa proses render iframe bar.com akan menerima klik, lalu mengirimkannya ke sana.
  2. Thread compositor untuk bar.com merutekan peristiwa click ke thread utama untuk bar.com dan menjadwalkan {i>event loop<i} rendering untuk memprosesnya.
  3. Pemroses peristiwa input untuk hit test thread utama bar.com guna menentukan Elemen DOM di iframe diklik, lalu mengaktifkan peristiwa click untuk diamati oleh skrip. Tidak mendengar preventDefault, fungsi ini akan membuka hyperlink.
  4. Setelah halaman tujuan hyperlink dimuat, status baru akan dirender, dengan langkah-langkah yang mirip dengan "render DOM yang diubah" contoh sebelumnya. (Perubahan berikutnya ini tidak digambarkan di sini.)

Bawa pulang

Butuh banyak waktu untuk mengingat dan menginternalisasi cara kerja rendering.

Hal terpenting untuk dipahami adalah bahwa pipeline rendering, melalui proses yang modularisasi dan perhatian terhadap detail, telah dibagi menjadi beberapa komponen yang berdiri sendiri. Komponen-komponen ini kemudian dibagi secara paralel proses dan thread untuk memaksimalkan performa skalabel dan ekstensibilitas.

Setiap komponen memainkan peran penting dalam memungkinkan kinerja dan fitur aplikasi web modern.

Teruslah membaca tentang struktur data utama, yang sama pentingnya untuk RenderingNG dengan komponen kode.


Ilustrasi oleh Una Kravets.