Arsitektur RenderingNG

Chris Harrelson
Chris Harrelson

Di sini Anda akan menemukan cara menyiapkan bagian komponen RenderingNG, dan cara pipeline rendering mengalir melaluinya.

Mulai dari tingkat tertinggi, tugas rendering adalah:

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

Konten yang akan dirender adalah hierarki frame untuk setiap tab browser, ditambah antarmuka browser. Selain itu, streaming peristiwa input mentah dari layar sentuh, mouse, keyboard, dan perangkat hardware lainnya.

Setiap frame mencakup:

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

Frame adalah dokumen HTML, beserta URL-nya. Halaman web yang dimuat di tab browser memiliki bingkai tingkat teratas, bingkai turunan untuk setiap iframe yang disertakan dalam dokumen tingkat teratas, dan turunan iframe rekursifnya.

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

Komponen arsitektur

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

Struktur pipeline rendering

Diagram pipeline rendering.
Panah menunjukkan input dan output dari setiap tahap. Tahap dinotasikan dengan warna, untuk menunjukkan thread atau proses yang dijalankan. Dalam beberapa kasus, tahap dapat dieksekusi di beberapa tempat, bergantung pada keadaan, itulah sebabnya beberapa tahap memiliki dua warna. Tahap hijau merender thread utama proses; kuning adalah kompositor proses render; tahap oranye adalah proses visualisasi.

Rendering berlangsung dalam pipeline dengan sejumlah tahap dan artefak yang dibuat selama proses. Setiap tahap mewakili kode yang melakukan satu tugas yang ditentukan dengan baik dalam rendering. Artefak adalah struktur data yang merupakan input atau output dari tahap.

Tahap-tahapnya adalah:

  1. Menganimasikan: mengubah gaya yang dikomputasi dan mengubah hierarki properti dari waktu ke waktu berdasarkan linimasa deklaratif.
  2. Gaya: menerapkan CSS ke DOM, dan membuat gaya yang dihitung.
  3. Tata letak: menentukan ukuran dan posisi elemen DOM di layar, dan membuat hierarki fragmen yang tidak dapat diubah.
  4. Pra-pengecatan: menghitung hierarki properti dan invalidate daftar tampilan dan ubin tekstur GPU yang ada sesuai kebutuhan.
  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 meraster ubin tekstur GPU dari DOM.
  7. Commit: menyalin hierarki properti dan daftar tampilan ke thread kompositor.
  8. Layerize: membagi daftar tampilan menjadi daftar lapisan gabungan untuk rasterisasi dan animasi independen.
  9. Raster, decode, dan paint worklet: masing-masing mengubah daftar tampilan, gambar yang dienkode, dan kode worklet lukisan menjadi kartu tekstur GPU.
  10. Aktifkan: membuat frame kompositor yang mewakili cara menggambar dan memosisikan kartu GPU ke layar, bersama dengan efek visual apa pun.
  11. Aggregate: menggabungkan frame kompositor dari semua frame kompositor yang terlihat menjadi satu frame kompositor global.
  12. Gambar: mengeksekusi frame kompositor gabungan di GPU untuk membuat piksel di layar.

Tahapan pipeline rendering dapat dilewati jika tidak diperlukan. Misalnya, animasi efek visual, dan scroll, dapat melewati tata letak, pra-pewarnaan, dan pewarnaan. Itulah sebabnya animasi dan scroll ditandai dengan titik kuning dan hijau dalam diagram. Jika tata letak, pra-cat, dan cat dapat dilewati untuk efek visual, tindakan tersebut dapat dijalankan sepenuhnya di thread kompositor dan melewati thread utama.

Rendering UI browser tidak digambarkan secara 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 dengan kode independen yang mendekode frame menjadi ubin tekstur GPU yang kemudian dicolokkan ke frame kompositor dan langkah gambar.

Struktur proses dan thread

Proses CPU

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

Diagram berbagai bagian proses CPU

  • Proses rendering merender, menganimasikan, men-scroll, dan merutekan input untuk satu situs dan kombinasi tab. Ada beberapa proses rendering.
  • 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. Ada satu proses browser.
  • Proses Viz menggabungkan komposisi dari beberapa proses render serta proses browser. Fungsi ini meraster dan menggambar menggunakan GPU. Ada satu proses Viz.

Situs yang berbeda selalu berakhir dalam proses rendering yang berbeda.

Beberapa tab atau jendela browser dari situs yang sama biasanya menggunakan proses render yang berbeda, kecuali jika tab tersebut terkait, seperti salah satu tab membuka tab lainnya. Dengan tekanan memori yang kuat di desktop, Chromium dapat menempatkan beberapa tab dari situs yang sama ke dalam proses rendering yang sama meskipun tidak terkait.

Dalam satu tab browser, frame dari situs yang berbeda selalu berada dalam proses render yang berbeda satu sama lain, tetapi frame dari situs yang sama selalu berada dalam proses render yang sama. Dari perspektif rendering, keunggulan penting dari beberapa proses render adalah iframe lintas situs dan tab mencapai isolasi performa dari satu sama lain. Selain itu, origin dapat memilih untuk lebih banyak isolasi.

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

Memisahkan Viz ke dalam prosesnya sendiri baik untuk stabilitas saat menghadapi bug dalam driver atau hardware GPU. Hal ini juga baik 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 UI browser untuk digambar, Anda mungkin bertanya-tanya: mengapa hanya ada satu proses browser? Alasannya adalah hanya satu tab yang difokuskan dalam satu waktu; bahkan, tab browser yang tidak terlihat sebagian besar dinonaktifkan dan menghapus semua memori GPU-nya. Namun, fitur rendering UI browser yang kompleks juga semakin banyak diterapkan dalam proses rendering (dikenal sebagai WebUI). Hal ini bukan karena 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 dengan aplikasi penyematan, dan WebView hanya memiliki satu proses rendering.

Terkadang juga ada proses utilitas untuk mendekode konten video yang dilindungi. Proses ini tidak digambarkan dalam diagram sebelumnya.

Thread

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

Diagram proses rendering.

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

Memisahkan thread utama dan kompositor sangat penting untuk isolasi performa animasi dan scroll dari pekerjaan thread utama.

Hanya ada satu thread utama per proses render, meskipun beberapa tab atau frame dari situs yang sama dapat 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 pula, hanya ada satu thread kompositor per proses render. Umumnya, tidak masalah jika hanya ada satu, karena semua operasi yang sangat mahal pada thread kompositor didelegasikan ke thread pekerja kompositor atau proses Viz, dan pekerjaan ini dapat dilakukan secara paralel dengan pemilihan rute input, scroll, atau animasi. Thread pekerja kompositor mengoordinasikan tugas yang berjalan dalam proses Viz, tetapi akselerasi GPU di mana saja dapat gagal karena alasan di luar kendali Chromium, seperti bug driver. Dalam situasi ini, thread pekerja akan melakukan pekerjaan dalam mode penggantian di CPU.

Jumlah thread pekerja kompositor bergantung pada kemampuan perangkat. Misalnya, desktop umumnya akan menggunakan lebih banyak thread, karena memiliki lebih banyak core CPU dan tidak terlalu dibatasi baterai dibandingkan perangkat seluler. Ini adalah contoh penskalaan ke atas dan penskalaan ke bawah.

Arsitektur threading proses render adalah penerapan tiga pola pengoptimalan yang berbeda:

  • Thread helper: mengirim subtugas yang berjalan lama ke thread tambahan agar thread induk tetap responsif terhadap permintaan serentak lainnya. Helper thread utama dan thread helper kompositor adalah contoh yang baik dari teknik ini.
  • Multiple buffering: menampilkan konten yang sebelumnya dirender saat merender konten baru, untuk menyembunyikan latensi rendering. Thread kompositor menggunakan teknik ini.
  • Parallelisasi pipeline: menjalankan pipeline rendering di beberapa tempat secara bersamaan. Inilah cara scroll dan animasi menjadi cepat; meskipun update rendering thread utama terjadi, scroll dan animasi dapat berjalan secara paralel.

Proses browser

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

  • Thread rendering dan komposisi merespons input di UI browser, me-rutekan input lain ke proses rendering yang benar; menata dan melukis UI browser.
  • Pembantu thread rendering dan komposisi menjalankan tugas dekode gambar dan raster atau dekode penggantian.

Thread rendering dan komposisi proses browser mirip dengan kode dan fungsi proses rendering, kecuali bahwa thread utama dan thread kompositor digabungkan menjadi satu. Dalam hal ini, hanya ada satu thread yang diperlukan karena tidak diperlukan isolasi performa dari tugas thread utama yang panjang, karena tidak ada desainnya.

Proses visualisasi

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

  • Raster thread utama GPU menampilkan daftar dan frame video ke dalam ubin tekstur GPU, dan menggambar frame kompositor ke layar.
  • Thread compositor tampilan menggabungkan dan mengoptimalkan komposisi dari setiap proses render, ditambah proses browser, menjadi satu frame compositor untuk presentasi ke layar.

Raster dan gambar umumnya terjadi pada thread yang sama, karena keduanya mengandalkan resource GPU, dan sulit untuk menggunakan GPU secara multi-thread dengan andal (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 terpisah untuk menggambar karena cara WebView disematkan ke aplikasi native. Platform lain kemungkinan akan memiliki thread tersebut pada masa mendatang.

Compositor tampilan berada di thread yang berbeda karena harus selalu responsif, dan tidak memblokir kemungkinan sumber pelambatan pada 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 kompositor proses render, ada komponen software logis yang berinteraksi satu sama lain dengan cara terstruktur.

Merender komponen thread utama proses

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 semua API ini.
  • Pemroses siklus proses dokumen mengeksekusi langkah-langkah pipeline rendering hingga dan termasuk langkah commit.
  • Komponen pengujian hit dan pengiriman peristiwa input menjalankan pengujian hit untuk mengetahui elemen DOM mana yang ditargetkan oleh peristiwa, dan menjalankan algoritma pengiriman peristiwa input dan perilaku default.

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

Diagram hierarki frame.

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

Anda dapat membayangkan mewarnai frame sesuai dengan proses rendernya. Pada gambar sebelumnya, lingkaran hijau adalah semua frame dalam satu proses render; lingkaran oranye berada di detik kedua, dan lingkaran biru berada di detik ketiga.

Fragmen hierarki frame lokal adalah komponen terhubung dengan warna yang sama dalam hierarki frame. Ada empat hierarki 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 hierarki frame lokal mungkin berada dalam proses rendering yang sama dengan hierarki frame lokal lainnya, atau mungkin tidak. Hal ini ditentukan oleh cara proses render dipilih, seperti yang dijelaskan sebelumnya.

Struktur thread kompositor proses render

Diagram yang menampilkan komponen kompositor proses render.

Komponen komposer proses render mencakup:

  • Pemroses data yang mempertahankan daftar lapisan gabungan, daftar tampilan, dan hierarki properti.
  • Runner siklus proses yang menjalankan animasi, scroll, komposit, raster, dan mendekode serta mengaktifkan langkah-langkah pipeline rendering. (Ingat bahwa animasi dan scroll dapat terjadi di thread utama dan kompositor.)
  • Pemroses input dan hit test melakukan pemrosesan input dan hit test pada resolusi lapisan gabungan, untuk menentukan apakah gestur scroll dapat dijalankan di thread kompositor, 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 bahas satu contoh dari empat tugas utama rendering. Pengingat:

  1. Merender konten menjadi piksel di layar.
  2. Animasikan efek visual pada konten dari satu status ke status lainnya.
  3. Scroll sebagai respons terhadap input.
  4. Arahkan 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 rendering untuk foo.com.
  2. Perender Blink memberi tahu kompositor bahwa render perlu dilakukan.
  3. Compositor memberi tahu Viz bahwa render harus terjadi.
  4. Viz menandakan awal rendering kembali ke kompositor.
  5. Compositor meneruskan sinyal mulai ke perender Blink.
  6. Runner loop peristiwa thread utama menjalankan siklus proses dokumen.
  7. Thread utama mengirimkan hasilnya ke thread kompositor.
  8. Runner loop peristiwa kompositor menjalankan siklus proses komposisi.
  9. Semua tugas raster dikirim ke Viz untuk raster (sering kali ada lebih dari satu tugas ini).
  10. Viz meraster konten di GPU.
  11. Viz mengonfirmasi penyelesaian tugas raster. Catatan: Chromium sering kali tidak menunggu raster selesai, dan sebagai gantinya menggunakan sesuatu yang disebut token sinkronisasi yang harus di-resolve oleh tugas raster sebelum langkah 15 dieksekusi.
  12. Frame kompositor dikirim ke Viz.
  13. Viz menggabungkan frame kompositor untuk proses rendering foo.com, proses rendering iframe bar.com, dan UI browser.
  14. Viz menjadwalkan hasil seri.
  15. Viz menggambar frame compositor gabungan ke layar.

Untuk animate transisi transformasi CSS di tab kedua:

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

Untuk men-scroll halaman web di tab ketiga:

  1. Urutan peristiwa input (mouse, sentuh, atau keyboard) masuk ke proses browser.
  2. Setiap peristiwa dirutekan ke thread komposer proses rendering baz.com.
  3. Kompositor menentukan apakah thread utama perlu mengetahui peristiwa tersebut.
  4. Peristiwa 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 menampilkan apakah preventDefault dipanggil ke kompositor.
  7. Jika tidak, peristiwa input akan dikirim kembali ke proses browser.
  8. Proses browser akan mengonversinya menjadi gestur scroll dengan menggabungkannya dengan peristiwa terbaru lainnya.
  9. Gestur scroll dikirim lagi ke thread komposer proses render baz.com,
  10. Scroll diterapkan di sana, dan thread kompositor untuk proses render bar.com menandai animasi dalam loop peristiwa kompositornya. Tindakan ini kemudian akan mengubah offset scroll di hierarki properti dan menjalankan ulang siklus proses kompositor. Tindakan ini juga memberi tahu thread utama untuk memicu peristiwa scroll (tidak digambarkan di sini).
  11. Frame kompositor dikirim ke Viz.
  12. Viz menggabungkan frame kompositor untuk proses rendering foo.com, proses rendering bar.com, dan UI browser.
  13. Viz menjadwalkan hasil seri.
  14. Viz menggambar frame compositor gabungan ke layar.

Untuk me-rutekan peristiwa click pada hyperlink di iframe #dua pada tab satu:

  1. Peristiwa input (mouse, sentuh, atau keyboard) muncul ke proses browser. Fungsi ini melakukan hit test perkiraan untuk menentukan bahwa proses render iframe bar.com harus menerima klik, dan mengirimkannya ke sana.
  2. Thread kompositor 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 hit test thread utama bar.com untuk menentukan elemen DOM mana di iframe yang diklik, dan memicu peristiwa click untuk diamati skrip. Jika tidak mendengar preventDefault, aplikasi akan membuka hyperlink.
  4. Setelah halaman tujuan hyperlink dimuat, status baru akan dirender, dengan langkah-langkah yang mirip dengan contoh sebelumnya "merender DOM yang diubah". (Perubahan berikutnya ini tidak digambarkan di sini.)

Hasil

Perlu waktu yang lama untuk mengingat dan memahami cara kerja rendering.

Hal terpenting yang dapat diambil adalah pipeline rendering, melalui modularisasi yang cermat dan perhatian terhadap detail, telah dibagi menjadi sejumlah komponen mandiri. Komponen ini kemudian telah dibagi di seluruh proses dan thread paralel untuk memaksimalkan performa skalabel dan peluang ekstensibilitas.

Setiap komponen memiliki peran penting dalam mengoptimalkan performa dan fitur aplikasi web modern.

Baca terus tentang struktur data utama, yang sama pentingnya dengan komponen kode untuk RenderingNG.


Ilustrasi oleh Una Kravets.