Pembahasan mendalam RenderingNG: BlinkNG

Stefan Zager
Stefan Zager
Chris Harrelson
Chris Harrelson

Blink mengacu pada implementasi platform web Chromium, dan mencakup semua fase rendering sebelum komposisi, yang diakhiri dengan commit komposer. Anda dapat membaca lebih lanjut arsitektur rendering blink di artikel sebelumnya dalam seri ini.

Blink dimulai sebagai fork dari WebKit, yang merupakan fork dari KHTML, yang berasal dari tahun 1998. File ini berisi beberapa kode tertua (dan paling penting) di Chromium, dan pada tahun 2014, kode ini sudah tidak relevan lagi. Pada tahun itu, kami memulai serangkaian project ambisius dengan nama yang kami sebut BlinkNG, dengan tujuan mengatasi kekurangan yang sudah lama ada dalam organisasi dan struktur kode Blink. Artikel ini akan membahas BlinkNG dan project penyusunnya: alasan kami melakukannya, pencapaiannya, prinsip panduan yang membentuk desainnya, dan peluang untuk peningkatan di masa mendatang yang dapat dilakukannya.

Pipeline rendering sebelum dan sesudah BlinkNG.

Rendering pra-NG

Pipeline rendering dalam Blink secara konseptual selalu dibagi menjadi beberapa fase (gaya, tata letak, cat, dan sebagainya), tetapi hambatan abstraksi bocor. Secara umum, data yang terkait dengan rendering terdiri dari objek yang dapat diubah dan berumur panjang. Objek ini dapat—dan telah—diubah kapan saja, dan sering didaur ulang dan digunakan kembali oleh update rendering berturut-turut. Tidak mungkin untuk menjawab pertanyaan sederhana seperti:

  • Apakah output gaya, tata letak, atau cat perlu diperbarui?
  • Kapan data ini akan mendapatkan nilai "akhir"?
  • Kapan data ini dapat diubah?
  • Kapan objek ini akan dihapus?

Ada banyak contohnya, termasuk:

Gaya akan menghasilkan ComputedStyle berdasarkan stylesheet; tetapi ComputedStyle tidak dapat diubah; dalam beberapa kasus, ComputedStyle akan diubah oleh tahap pipeline berikutnya.

Gaya akan menghasilkan hierarki LayoutObject, lalu tata letak akan menganotasi objek tersebut dengan informasi ukuran dan pemosisian. Dalam beberapa kasus, tata letak bahkan akan mengubah struktur hierarki. Tidak ada pemisahan yang jelas antara input dan output tata letak.

Gaya akan menghasilkan struktur data aksesori yang menentukan jalannya komposisi, dan struktur data tersebut diubah di tempat oleh setiap fase setelah gaya.

Pada tingkat yang lebih rendah, jenis data rendering sebagian besar terdiri dari hierarki khusus (misalnya, hierarki DOM, hierarki gaya, hierarki tata letak, hierarki properti cat); dan fase rendering diterapkan sebagai penelusuran hierarki rekursif. Idealnya, perjalanan hierarki harus terbatas: saat memproses node hierarki tertentu, kita tidak boleh mengakses informasi apa pun di luar subhierarki yang berakar di node tersebut. Hal ini tidak pernah terjadi sebelum RenderingNG; penelusuran hierarki sering kali mengakses informasi dari ancestor node yang sedang diproses. Hal ini membuat sistem menjadi sangat rapuh dan rentan terhadap error. Anda juga tidak dapat memulai perjalanan hierarki dari mana pun selain dari root hierarki.

Terakhir, ada banyak on-ramp ke pipeline rendering yang tersebar di seluruh kode: tata letak paksa yang dipicu oleh JavaScript, update parsial yang dipicu selama pemuatan dokumen, update paksa untuk mempersiapkan penargetan peristiwa, update terjadwal yang diminta oleh sistem tampilan, dan API khusus yang hanya ditampilkan untuk menguji kode, untuk menyebutkan beberapa. Bahkan ada beberapa jalur rekursif dan reentrant ke pipeline rendering (yaitu, melompat ke awal satu tahap dari tengah tahap lainnya). Setiap on-ramp ini memiliki perilaku idiosinkratiknya sendiri, dan dalam beberapa kasus, output rendering akan bergantung pada cara update rendering dipicu.

Yang kami ubah

BlinkNG terdiri dari banyak sub-project, besar dan kecil, semuanya dengan sasaran bersama untuk menghilangkan defisit arsitektur yang dijelaskan sebelumnya. Project ini memiliki beberapa prinsip panduan yang dirancang untuk membuat pipeline rendering lebih mirip dengan pipeline sebenarnya:

  • Titik entri seragam: Kita harus selalu memasuki pipeline di awal.
  • Tahap fungsional: Setiap tahap harus memiliki input dan output yang ditentukan dengan baik, dan perilakunya harus fungsional, yaitu deterministik dan dapat diulang, dan output hanya boleh bergantung pada input yang ditentukan.
  • Input konstan: Input setiap tahap harus konstan secara efektif saat tahap berjalan.
  • Output yang tidak dapat diubah: Setelah tahap selesai, output-nya tidak boleh diubah selama sisa update rendering.
  • Konsistensi checkpoint: Di akhir setiap tahap, data rendering yang dihasilkan sejauh ini harus berada dalam status yang konsisten dengan sendirinya.
  • Penghapusan duplikat tugas: Hanya komputasi setiap hal sekali.

Daftar lengkap sub-project BlinkNG akan membuat pembacaan menjadi membosankan, tetapi berikut adalah beberapa konsekuensi tertentu.

Siklus proses dokumen

Class DocumentLifecycle melacak progres kita melalui pipeline rendering. Hal ini memungkinkan kita melakukan pemeriksaan dasar yang menerapkan invarian yang tercantum sebelumnya, seperti:

  • Jika kita mengubah properti ComputedStyle, siklus proses dokumen harus kInStyleRecalc.
  • Jika status DocumentLifecycle adalah kStyleClean atau yang lebih baru, NeedsStyleRecalc() harus menampilkan false untuk node yang dilampirkan.
  • Saat memasuki fase siklus proses cat, status siklus proses harus kPrePaintClean.

Selama menerapkan BlinkNG, kami secara sistematis menghilangkan jalur kode yang melanggar invarian ini, dan menambahkan banyak pernyataan lainnya di seluruh kode untuk memastikan kita tidak mengalami regresi.

Jika pernah melihat kode rendering tingkat rendah, Anda mungkin bertanya-tanya, "Bagaimana saya bisa sampai di sini?" Seperti yang disebutkan sebelumnya, ada berbagai titik entri ke pipeline rendering. Sebelumnya, hal ini mencakup jalur panggilan rekursif dan reentrant, serta tempat kita memasuki pipeline pada fase perantara, bukan memulai dari awal. Dalam proses BlinkNG, kami menganalisis jalur panggilan ini dan menentukan bahwa semuanya dapat direduksi menjadi dua skenario dasar:

  • Semua data rendering perlu diperbarui—misalnya, saat membuat piksel baru untuk ditampilkan atau melakukan hit test untuk penargetan peristiwa.
  • Kita memerlukan nilai terbaru untuk kueri tertentu yang dapat dijawab tanpa memperbarui semua data rendering. Hal ini mencakup sebagian besar kueri JavaScript, misalnya, node.offsetTop.

Sekarang hanya ada dua titik entri ke pipeline rendering, yang sesuai dengan kedua skenario ini. Jalur kode reentrant telah dihapus atau difaktorkan ulang, dan Anda tidak dapat lagi memasuki pipeline yang dimulai pada fase perantara. Hal ini telah menghilangkan banyak misteri tentang kapan dan bagaimana pembaruan rendering terjadi, sehingga mempermudah alasan perilaku sistem.

Gaya, tata letak, dan pra-pewarnaan pipeline

Secara kolektif, fase rendering sebelum cat bertanggung jawab atas hal berikut:

  • Menjalankan algoritma cascade gaya untuk menghitung properti gaya akhir untuk node DOM.
  • Membuat hierarki tata letak yang mewakili hierarki kotak dokumen.
  • Menentukan informasi ukuran dan posisi untuk semua kotak.
  • Membulatkan atau menyelaraskan geometri sub-piksel ke batas seluruh piksel untuk menggambar.
  • Menentukan properti lapisan gabungan (transformasi afin, filter, opasitas, atau apa pun yang dapat diakselerasi GPU).
  • Menentukan konten yang telah berubah sejak fase paint sebelumnya, dan perlu di-paint atau dicat ulang (pembatalan validasi paint).

Daftar ini belum berubah, tetapi sebelum BlinkNG, sebagian besar pekerjaan ini dilakukan secara ad hoc, yang tersebar di beberapa fase rendering, dengan banyak fungsi duplikat dan inefisiensi bawaan. Misalnya, fase gaya selalu bertanggung jawab untuk menghitung properti gaya akhir untuk node, tetapi ada beberapa kasus khusus saat kita tidak menentukan nilai properti gaya akhir hingga setelah fase gaya selesai. Tidak ada titik formal atau yang dapat ditegakkan dalam proses rendering yang dapat kami katakan dengan pasti bahwa informasi gaya sudah lengkap dan tidak dapat diubah.

Contoh bagus lainnya dari masalah pra-BlinkNG adalah pembatalan validasi cat. Sebelumnya, pembatalan validasi cat tersebar di semua fase rendering yang mengarah ke cat. Saat mengubah kode gaya atau tata letak, sulit untuk mengetahui perubahan logika pembatalan validasi yang diperlukan, dan mudah untuk membuat kesalahan yang menyebabkan bug pembatalan validasi yang kurang atau berlebihan. Anda dapat membaca selengkapnya tentang kerumitan sistem pembatalan validasi cat lama dalam artikel dari seri ini yang ditujukan untuk LayoutNG.

Mengaitkan geometri tata letak subpiksel ke seluruh batas piksel untuk menggambar adalah contoh tempat kami memiliki beberapa implementasi fungsi yang sama, dan melakukan banyak pekerjaan yang berlebihan. Ada satu jalur kode snap piksel yang digunakan oleh sistem cat, dan jalur kode yang sepenuhnya terpisah yang digunakan setiap kali kita memerlukan penghitungan koordinat snap piksel satu kali secara langsung di luar kode cat. Tidak perlu dikatakan, setiap implementasi memiliki bugnya sendiri, dan hasilnya tidak selalu cocok. Karena tidak ada penyimpanan dalam cache untuk informasi ini, sistem terkadang akan melakukan komputasi yang sama persis berulang kali—beban lain pada performa.

Berikut adalah beberapa project penting yang menghilangkan defisit arsitektur fase rendering sebelum proses menggambar.

Project Squad: Membuat pipeline fase gaya

Project ini mengatasi dua defisit utama dalam fase gaya yang mencegahnya di-pipeline dengan rapi:

Ada dua output utama dari fase gaya: ComputedStyle, yang berisi hasil dari menjalankan algoritma cascade CSS di atas hierarki DOM; dan hierarki LayoutObjects, yang menetapkan urutan operasi untuk fase tata letak. Secara konseptual, menjalankan algoritma cascade harus dilakukan secara ketat sebelum membuat hierarki tata letak; tetapi sebelumnya, kedua operasi ini saling tumpang-tindih. Project Squad berhasil membagi kedua hal tersebut menjadi fase berurutan yang berbeda.

Sebelumnya, ComputedStyle tidak selalu mendapatkan nilai akhirnya selama penghitungan ulang gaya; ada beberapa situasi saat ComputedStyle diperbarui selama fase pipeline berikutnya. Project Squad berhasil memfaktorkan ulang jalur kode ini, sehingga ComputedStyle tidak pernah diubah setelah fase gaya.

LayoutNG: Membuat pipeline fase tata letak

Proyek monumental ini—salah satu fondasi RenderingNG—adalah penulisan ulang lengkap fase rendering tata letak. Kita tidak akan membahas seluruh project di sini, tetapi ada beberapa aspek penting untuk keseluruhan project BlinkNG:

  • Sebelumnya, fase tata letak menerima hierarki LayoutObject yang dibuat oleh fase gaya, dan menganotasi hierarki dengan informasi ukuran dan posisi. Dengan demikian, tidak ada pemisahan yang jelas antara input dan output. LayoutNG memperkenalkan hierarki fragmen, yang merupakan output utama tata letak yang hanya dapat dibaca, dan berfungsi sebagai input utama untuk fase rendering berikutnya.
  • LayoutNG menghadirkan properti pembatasan ke tata letak: saat menghitung ukuran dan posisi LayoutObject tertentu, kita tidak lagi melihat ke luar sub-pohon yang berakar pada objek tersebut. Semua informasi yang diperlukan untuk memperbarui tata letak objek tertentu dihitung terlebih dahulu dan diberikan sebagai input hanya baca ke algoritma.
  • Sebelumnya, ada kasus ekstrem saat algoritma tata letak tidak berfungsi secara ketat: hasil algoritma bergantung pada update tata letak sebelumnya yang terbaru. LayoutNG menghilangkan kasus tersebut.

Fase pra-proses gambar

Sebelumnya, tidak ada fase rendering pra-proses cat formal, hanya kumpulan operasi pasca-tata letak. Fase pra-proses gambar muncul karena adanya beberapa fungsi terkait yang dapat diterapkan dengan sebaik mungkin sebagai penelusuran hierarki tata letak yang sistematis setelah tata letak selesai; yang paling penting:

  • Mengeluarkan pembatalan validasi cat: Sangat sulit untuk melakukan pembatalan validasi cat dengan benar selama proses tata letak, jika kita memiliki informasi yang tidak lengkap. Hal ini jauh lebih mudah untuk dilakukan dengan benar, dan dapat sangat efisien, jika dibagi menjadi dua proses yang berbeda: selama gaya dan tata letak, konten dapat ditandai dengan flag boolean sederhana sebagai "mungkin memerlukan pembatalan validasi cat". Selama proses penelusuran hierarki pra-proses gambar, kita memeriksa tanda ini dan mengeluarkan pembatalan validasi sesuai kebutuhan.
  • Membuat hierarki properti cat: Proses yang dijelaskan secara lebih mendetail nanti.
  • Menghitung dan merekam lokasi cat yang diambil piksel: Hasil yang direkam dapat digunakan oleh fase cat, dan juga oleh kode downstream yang membutuhkannya, tanpa komputasi yang berlebihan.

Hierarki properti: Geometri yang konsisten

Hierarki properti diperkenalkan lebih awal di RenderingNG untuk menangani kompleksitas scroll, yang di web memiliki struktur yang berbeda dari semua jenis efek visual lainnya. Sebelum hierarki properti, komposer Chromium menggunakan hierarki "lapisan" tunggal untuk merepresentasikan hubungan geometris konten gabungan, tetapi hal itu dengan cepat hancur karena kompleksitas penuh fitur seperti position:fixed menjadi jelas. Hierarki lapisan mengembangkan pointer non-lokal tambahan yang menunjukkan "induk scroll" atau "induk klip" lapisan, dan tidak lama kemudian kode tersebut sangat sulit dipahami.

Hierarki properti memperbaiki hal ini dengan merepresentasikan aspek scroll dan klip konten yang berlebihan secara terpisah dari semua efek visual lainnya. Hal ini memungkinkan pemodelan struktur visual dan scroll situs yang sebenarnya dengan benar. Selanjutnya, "semua" yang harus kita lakukan adalah menerapkan algoritma di atas hierarki properti, seperti transformasi ruang layar dari lapisan gabungan, atau menentukan lapisan mana yang di-scroll dan mana yang tidak.

Bahkan, kami segera menyadari bahwa ada banyak tempat lain dalam kode tempat pertanyaan geometris serupa diajukan. (Postingan struktur data utama memiliki daftar yang lebih lengkap.) Beberapa di antaranya memiliki implementasi duplikat dari hal yang sama dengan yang dilakukan kode kompositor; semuanya memiliki subset bug yang berbeda; dan tidak satu pun dari mereka yang memodelkan struktur situs yang sebenarnya dengan benar. Solusi kemudian menjadi jelas: memusatkan semua algoritma geometri di satu tempat dan memfaktorkan ulang semua kode untuk menggunakannya.

Algoritma ini pada akhirnya bergantung pada hierarki properti, itulah sebabnya hierarki properti adalah struktur data kunci–yaitu, yang digunakan di seluruh pipeline–RenderingNG. Jadi, untuk mencapai tujuan kode geometri terpusat ini, kita perlu memperkenalkan konsep hierarki properti jauh lebih awal dalam pipeline–di pra-proses gambar–dan mengubah semua API yang sekarang bergantung padanya agar memerlukan pra-proses gambar dijalankan sebelum dapat dieksekusi.

Cerita ini adalah aspek lain dari pola pemfaktoran ulang BlinkNG: mengidentifikasi komputasi utama, memfaktorkan ulang untuk menghindari duplikasinya, dan membuat tahap pipeline yang jelas yang membuat struktur data yang memberi makan. Kami menghitung hierarki properti tepat pada saat semua informasi yang diperlukan tersedia; dan kami memastikan bahwa hierarki properti tidak dapat berubah saat tahap rendering berikutnya berjalan.

Komposisi setelah proses paint: Pemrosesan paint dan komposisi secara paralel

Pembuatan lapisan adalah proses untuk mengetahui konten DOM mana yang masuk ke lapisan gabungannya sendiri (yang pada akhirnya mewakili tekstur GPU). Sebelum RenderingNG, pembuatan lapisan berjalan sebelum proses menggambar, bukan setelahnya (lihat di sini untuk pipeline saat ini–perhatikan perubahan urutan). Pertama-tama, kita akan menentukan bagian DOM mana yang masuk ke lapisan gabungan, lalu baru menggambar daftar tampilan untuk tekstur tersebut. Tentu saja, keputusan tersebut bergantung pada faktor-faktor seperti elemen DOM mana yang dianimasikan atau di-scroll, atau memiliki transformasi 3D, dan elemen mana yang digambar di atasnya.

Hal ini menyebabkan masalah besar, karena kurang lebih memerlukan adanya dependensi melingkar dalam kode, yang merupakan masalah besar untuk pipeline rendering. Mari kita lihat alasannya melalui contoh. Misalkan kita perlu membatalkan validasi cat (artinya kita perlu menggambar ulang daftar tampilan, lalu merasternya lagi). Kebutuhan untuk membatalkan validasi dapat berasal dari perubahan pada DOM, atau dari gaya atau tata letak yang diubah. Namun, tentu saja, kita hanya ingin membatalkan bagian yang benar-benar telah berubah. Artinya, Anda harus mencari tahu lapisan gabungan mana yang terpengaruh, lalu membatalkan sebagian atau semua daftar tampilan untuk lapisan tersebut.

Artinya, pembatalan validasi bergantung pada keputusan DOM, gaya, tata letak, dan pelapisan sebelumnya (masa lalu: artinya untuk frame yang dirender sebelumnya). Namun, pembuatan lapisan saat ini juga bergantung pada semua hal tersebut. Selain itu, karena kami tidak memiliki dua salinan semua data pembuatan lapisan, sulit untuk membedakan antara keputusan pembuatan lapisan sebelumnya dan mendatang. Jadi, kita akhirnya memiliki banyak kode yang memiliki penalaran melingkar. Hal ini terkadang menyebabkan kode yang tidak logis atau salah, atau bahkan error atau masalah keamanan, jika kita tidak sangat berhati-hati.

Untuk mengatasi situasi ini, sejak awal kita memperkenalkan konsep objek DisableCompositingQueryAsserts. Sering kali, jika kode mencoba membuat kueri keputusan pembuatan lapisan sebelumnya, kode tersebut akan menyebabkan kegagalan pernyataan dan membuat browser error jika berada dalam mode debug. Hal ini membantu kami menghindari munculnya bug baru. Dan dalam setiap kasus saat kode secara sah diperlukan untuk membuat kueri keputusan pembuatan lapisan sebelumnya, kami memasukkan kode untuk mengizinkannya dengan mengalokasikan objek DisableCompositingQueryAsserts.

Rencana kami adalah, seiring waktu, menghapus semua objek DisableCompositingQueryAssert situs panggilan, lalu mendeklarasikan kode yang aman dan benar. Namun, yang kami temukan adalah sejumlah panggilan pada dasarnya tidak dapat dihapus selama pembuatan lapisan terjadi sebelum proses menggambar. (Kami akhirnya dapat menghapusnya baru-baru ini!) Ini adalah alasan pertama yang ditemukan untuk project Composite After Paint. Yang kita pelajari adalah, meskipun Anda memiliki fase pipeline yang ditentukan dengan baik untuk suatu operasi, jika berada di tempat yang salah dalam pipeline, Anda pada akhirnya akan mengalami error.

Alasan kedua untuk project Composite After Paint adalah bug Fundamental Compositing. Salah satu cara untuk menyatakan bug ini adalah bahwa elemen DOM bukan representasi 1:1 yang baik dari skema pelapisan yang efisien atau lengkap untuk konten halaman web. Dan karena komposisi dilakukan sebelum proses paint, komposisi ini secara inheren bergantung pada elemen DOM, bukan daftar tampilan atau hierarki properti. Hal ini sangat mirip dengan alasan kami memperkenalkan hierarki properti, dan seperti halnya hierarki properti, solusinya akan langsung muncul jika Anda mengetahui fase pipeline yang tepat, menjalankannya pada waktu yang tepat, dan memberinya struktur data utama yang benar. Dan seperti halnya hierarki properti, ini adalah peluang yang baik untuk menjamin bahwa setelah fase gambar selesai, outputnya tidak dapat diubah untuk semua fase pipeline berikutnya.

Manfaat

Seperti yang telah Anda lihat, pipeline rendering yang ditentukan dengan baik akan memberikan manfaat jangka panjang yang sangat besar. Bahkan ada lebih banyak lagi:

  • Keandalan yang sangat meningkat: Ini cukup mudah. Kode yang lebih rapi dengan antarmuka yang jelas dan dapat dipahami lebih mudah dipahami, ditulis, dan diuji. Hal ini membuatnya lebih andal. Hal ini juga membuat kode lebih aman dan lebih stabil, dengan lebih sedikit error dan lebih sedikit bug use-after-free.
  • Cakupan pengujian yang diperluas: Selama BlinkNG, kami menambahkan banyak sekali pengujian baru ke rangkaian pengujian kami. Hal ini mencakup pengujian unit yang memberikan verifikasi internal yang terfokus; pengujian regresi yang mencegah kita memperkenalkan kembali bug lama yang telah diperbaiki (sangat banyak); dan banyak penambahan ke rangkaian Pengujian Platform Web publik yang dikelola secara kolektif, yang digunakan semua browser untuk mengukur kepatuhan terhadap standar web.
  • Lebih mudah diperluas: Jika sistem dibagi menjadi komponen yang jelas, Anda tidak perlu memahami komponen lain pada tingkat detail apa pun untuk membuat progres pada komponen saat ini. Hal ini memudahkan semua orang untuk menambahkan nilai ke kode rendering tanpa harus menjadi pakar yang mendalam, dan juga memudahkan untuk memahami perilaku seluruh sistem.
  • Performa: Mengoptimalkan algoritma yang ditulis dalam kode spaghetti cukup sulit, tetapi hampir tidak mungkin untuk mencapai hal yang lebih besar seperti animasi dan scroll dengan thread universal atau proses dan thread untuk isolasi situs tanpa pipeline tersebut. Paralelisme dapat membantu kita meningkatkan performa secara signifikan, tetapi juga sangat rumit.
  • Pemberian dan pembatasan: Ada beberapa fitur baru yang dimungkinkan oleh BlinkNG yang menjalankan pipeline dengan cara baru dan inovatif. Misalnya, bagaimana jika kita hanya ingin menjalankan pipeline rendering hingga anggaran habis masa berlakunya? Atau melewatkan rendering untuk sub-pohon yang diketahui tidak relevan bagi pengguna saat ini? Itulah yang diaktifkan oleh properti CSS content-visibility. Bagaimana cara membuat gaya komponen bergantung pada tata letaknya? Itu adalah kueri penampung.

Studi kasus: Kueri penampung

Kueri penampung adalah fitur platform web mendatang yang sangat dinantikan (ini adalah fitur nomor satu yang paling banyak diminta dari developer CSS selama bertahun-tahun). Jika begitu bagus, mengapa belum ada? Alasannya adalah karena implementasi kueri penampung memerlukan pemahaman dan kontrol yang sangat cermat terhadap hubungan antara kode gaya dan tata letak. Mari kita pelajari lebih lanjut.

Kueri penampung memungkinkan gaya yang berlaku untuk elemen bergantung pada ukuran ancestor yang ditata. Karena ukuran yang ditata dihitung selama tata letak, artinya kita perlu menjalankan penghitungan ulang gaya setelah tata letak; tetapi penghitungan ulang gaya berjalan sebelum tata letak. Paradoks ayam dan telur ini adalah seluruh alasan mengapa kita tidak dapat menerapkan kueri penampung sebelum BlinkNG.

Bagaimana cara mengatasi masalah ini? Bukankah ini adalah dependensi pipeline mundur, yaitu masalah yang sama dengan yang dipecahkan oleh project seperti Composite After Paint? Lebih buruk lagi, bagaimana jika gaya baru mengubah ukuran ancestor? Apakah hal ini terkadang tidak akan menyebabkan loop tak terbatas?

Pada prinsipnya, dependensi melingkar dapat dipecahkan dengan menggunakan properti CSS contain, yang memungkinkan rendering di luar elemen tidak bergantung pada rendering dalam sub-pohon elemen tersebut. Artinya, gaya baru yang diterapkan oleh penampung tidak dapat memengaruhi ukuran penampung, karena kueri penampung memerlukan pembatasan.

Namun, sebenarnya, hal itu tidak cukup, dan perlu memperkenalkan jenis pembatasan yang lebih lemah daripada pembatasan ukuran saja. Hal ini karena biasanya Anda ingin penampung kueri penampung dapat mengubah ukuran hanya dalam satu arah (biasanya blok) berdasarkan dimensi inline-nya. Jadi, konsep pembatasan ukuran inline ditambahkan. Namun, seperti yang dapat Anda lihat dari catatan yang sangat panjang di bagian tersebut, selama ini tidak jelas apakah pembatasan ukuran inline dapat dilakukan.

Menjelaskan pembatasan dalam bahasa spesifikasi abstrak adalah satu hal, dan menerapkannya dengan benar adalah hal lain. Ingat bahwa salah satu tujuan BlinkNG adalah menerapkan prinsip pembatasan ke penelusuran hierarki yang membentuk logika utama rendering: saat menjelajahi sub-hierarki, tidak ada informasi yang diperlukan dari luar sub-hierarki. Seperti yang terjadi (sebenarnya bukan kebetulan), penerapan pembatasan CSS akan jauh lebih bersih dan mudah jika kode rendering mematuhi prinsip pembatasan.

Masa depan: komposisi di luar thread utama … dan seterusnya!

Pipeline rendering yang ditampilkan di sini sebenarnya sedikit lebih maju dari implementasi RenderingNG saat ini. Ini menunjukkan bahwa pembuatan lapisan berada di luar thread utama, padahal saat ini masih berada di thread utama. Namun, ini hanya masalah waktu sebelum hal ini dilakukan, setelah Composite After Paint dikirim dan pembuatan lapisan dilakukan setelah proses menggambar.

Untuk memahami mengapa hal ini penting, dan ke mana hal ini dapat mengarah, kita perlu mempertimbangkan arsitektur mesin rendering dari sudut pandang yang agak lebih tinggi. Salah satu kendala paling tahan lama untuk meningkatkan performa Chromium adalah fakta sederhana bahwa thread utama perender menangani logika aplikasi utama (yaitu, menjalankan skrip) dan sebagian besar rendering. Akibatnya, thread utama sering kali dipenuhi dengan pekerjaan, dan kemacetan thread utama sering kali menjadi bottleneck di seluruh browser.

Kabar baiknya adalah Anda tidak harus melakukannya dengan cara ini. Aspek arsitektur Chromium ini sudah ada sejak KHTML, saat eksekusi single-thread adalah model pemrograman yang dominan. Pada saat prosesor multi-core menjadi umum di perangkat kelas konsumen, asumsi single-thread telah diintegrasikan sepenuhnya ke dalam Blink (sebelumnya WebKit). Kami telah ingin memperkenalkan lebih banyak threading ke dalam mesin rendering sejak lama, tetapi hal itu tidak mungkin dilakukan di sistem lama. Salah satu tujuan utama Rendering NG adalah untuk keluar dari masalah ini, dan memungkinkan pemindahan tugas rendering, sebagian atau seluruhnya, ke thread lain.

Setelah BlinkNG mendekati penyelesaian, kami sudah mulai menjelajahi area ini; Non-Blocking Commit adalah upaya pertama untuk mengubah model threading perender. Commit komposer (atau hanya commit) adalah langkah sinkronisasi antara thread utama dan thread komposer. Selama commit, kita membuat salinan data rendering yang dihasilkan di thread utama, untuk digunakan oleh kode komposisi downstream yang berjalan di thread kompositor. Saat sinkronisasi ini terjadi, eksekusi thread utama dihentikan saat kode penyalinan berjalan di thread kompositor. Hal ini dilakukan untuk memastikan bahwa thread utama tidak mengubah data renderingnya saat thread kompositor menyalinnya.

Commit Non-Blocking akan menghilangkan kebutuhan thread utama untuk berhenti dan menunggu tahap commit berakhir—thread utama akan terus melakukan pekerjaan saat commit berjalan secara serentak di thread kompositor. Efek bersih dari Commit Non-Blocking adalah pengurangan waktu yang dikhususkan untuk merender pekerjaan di thread utama, yang akan mengurangi kemacetan di thread utama, dan meningkatkan performa. Saat ini (Maret 2022), kami memiliki prototipe Non-Blocking Commit yang berfungsi, dan kami sedang bersiap untuk melakukan analisis mendetail tentang dampaknya terhadap performa.

Yang sedang menunggu adalah Komposisi Off-main-thread, dengan tujuan membuat mesin rendering cocok dengan ilustrasi dengan memindahkan pembuatan lapisan dari thread utama, dan ke thread pekerja. Seperti Commit Non-Blocking, hal ini akan mengurangi kemacetan pada thread utama dengan mengurangi beban kerja renderingnya. Project seperti ini tidak akan pernah mungkin tanpa peningkatan arsitektur Composite After Paint.

Dan masih banyak lagi project yang sedang dalam proses. Kami akhirnya memiliki fondasi yang memungkinkan eksperimen dengan mendistribusikan ulang tugas rendering, dan kami sangat antusias untuk melihat apa yang dapat dilakukan.