Optimalisasi rendering adegan dari kartun Disney "Moana". Bagian 1

Walt Disney Animation Studios (WDAS) baru-baru ini membuat hadiah yang tak ternilai bagi komunitas riset rendering dengan merilis adegan pulau penuh dari kartun Moana . Geometri dan tekstur untuk satu bingkai menempati lebih dari 70 GB ruang disk. Ini adalah contoh hebat tingkat kerumitan yang harus dihadapi sistem rendering hari ini; tidak pernah sebelumnya para peneliti dan pengembang yang terlibat dalam rendering di luar studio film dapat bekerja dengan adegan yang realistis.

Beginilah hasil dari menampilkan adegan dengan pbrt modern seperti:


Sebuah pulau dari Moana dibuat oleh pbrt-v3 pada resolusi 2048x858 dengan 256 sampel per piksel. Total waktu render pada instance 12-core / 24-thread dari Google Compute Engine dengan frekuensi 2 GHz dengan versi terbaru dari pbrt-v3 adalah 1 jam 44 menit 45 detik.

Di pihak Disney, itu adalah pekerjaan yang sangat besar, ia harus mengekstraksi adegan itu dari format internalnya sendiri dan mengubahnya ke format yang biasa; Terima kasih khusus kepadanya untuk waktu yang dihabiskan untuk pengemasan dan mempersiapkan data ini untuk penggunaan luas. Saya yakin bahwa pekerjaan mereka akan dihargai dengan baik di masa depan karena peneliti menggunakan adegan ini untuk mempelajari masalah rendering adegan efisien tingkat kompleksitas ini.

Adegan ini telah banyak mengajari saya dan memungkinkan saya untuk meningkatkan penyaji pbrt, tetapi sebelum kita membahas ini, saya akan menceritakan sebuah kisah pendek untuk memahami konteksnya.

Hash itu bukan


Bertahun-tahun yang lalu, saat melakukan magang di tim rendering Pixar, saya mendapat pelajaran yang aneh: hal-hal β€œmenarik” hampir selalu muncul ketika input data dilewatkan ke sistem program yang sangat berbeda dari semua yang sebelumnya. Bahkan dalam sistem perangkat lunak yang ditulis dengan baik dan matang, tipe input baru hampir selalu mengarah pada penemuan cacat yang tidak diketahui dalam implementasi yang ada.

Saya pertama kali belajar pelajaran ini selama produksi Toy Story 2 . Suatu hari, seseorang memperhatikan bahwa banyak waktu yang dihabiskan untuk mengurai file deskripsi adegan RIB. Orang lain dari tim rendering (saya kira itu adalah Craig Kolb) meluncurkan profiler dan mulai mencari tahu.

Ternyata sebagian besar waktu parsing ditempati oleh pencarian di tabel hash yang digunakan untuk string interning . Tabel hash agak kecil, mungkin 256 elemen, dan ketika beberapa nilai di hash menjadi satu sel, ia mengatur sebuah rantai. Setelah implementasi pertama dari tabel hash, banyak waktu berlalu dan sekarang ada puluhan ribu objek di layar, sehingga meja kecil seperti itu dengan cepat diisi dan menjadi tidak efektif.

Itu paling disarankan untuk hanya menambah ukuran meja - semua ini terjadi pada puncak alur kerja, jadi tidak ada waktu untuk beberapa jenis solusi elegan, misalnya, memperluas ukuran meja ketika mengisinya. Kami membuat perubahan dalam satu baris, membangun kembali aplikasi, melakukan tes cepat sebelum melakukan dan ... tidak ada peningkatan kecepatan terjadi. Mencari tabel hash membutuhkan waktu yang sama. Luar biasa!

Setelah penelitian lebih lanjut, kami menemukan bahwa fungsi tabel hash yang digunakan mirip dengan yang berikut:

int hash(const char *str) { return str[0]; } 

(Maafkan saya, Pixar, jika saya mengungkapkan kode sumber RenderMan rahasia Anda.)

Fungsi "hash" diimplementasikan kembali pada 1980-an. Pada saat itu, programmer mungkin menganggap bahwa biaya komputasi untuk memeriksa efek dari semua karakter dalam string pada nilai hash akan terlalu tinggi dan tidak sepadan. (Saya pikir jika hanya ada beberapa objek dan 256 elemen dalam tabel hash dalam adegan, maka itu sudah cukup.)

Implementasi usang lainnya berkontribusi: dari saat Pixar mulai membuat filmnya, nama-nama objek dalam adegan telah tumbuh cukup banyak, misalnya, "BuzzLightyear / LeftArm / Hand / IndexFinger / Knuckle2". Namun, beberapa tahap awal pipa menggunakan penyangga dengan panjang tetap untuk menyimpan nama-nama objek dan memperpendek semua nama panjang, hanya mempertahankan ujungnya, dan, jika beruntung, menambahkan elipsis di awal, menjadikannya jelas bahwa sebagian dari nama itu hilang: "... tahun / KiriArm / Hand / IndexFinger / Knuckle2 ".

Selanjutnya, semua nama objek yang dilihat oleh renderer memiliki bentuk ini, fungsi hash menjadikannya semua menjadi satu bagian dari memori sebagai ".", Dan tabel hash sebenarnya adalah daftar terkait yang besar. Hari tua yang indah Setidaknya, setelah menemukan jawabannya, kami segera memperbaiki kesalahan ini.

Inovasi yang menarik


Pelajaran ini diingat saya tahun lalu ketika Heather Pritchet dan Rasmus Tamstorf dari WDAS menghubungi saya dan bertanya apakah saya akan tertarik untuk memeriksa kemungkinan kualitas rendering adegan dari Moana di halaman 1 . Secara alami, saya setuju. Saya senang membantu dan saya bertanya-tanya bagaimana semuanya akan berubah.

Optimis yang naif dalam diri saya berharap bahwa tidak akan ada kejutan besar - pada akhirnya, versi pertama pbrt dirilis sekitar 15 tahun yang lalu, dan banyak orang menggunakan dan mempelajari kodenya selama bertahun-tahun. Anda dapat yakin bahwa tidak akan ada gangguan seperti fungsi hash lama dari RenderMan, bukan?

Tentu saja jawabannya tidak. (Dan itulah sebabnya saya menulis ini dan beberapa posting lainnya.) Meskipun saya sedikit kecewa bahwa pbrt tidak sempurna "di luar kotak", tapi saya pikir pengalaman saya dengan adegan Moana adalah konfirmasi pertama dari nilai penerbitan adegan ini. ; pbrt telah menjadi sistem yang lebih baik karena saya tahu cara menangani adegan ini.

Rendering pertama


Setelah mengakses adegan, saya langsung mengunduhnya (butuh beberapa jam dengan koneksi internet rumah saya) dan membukanya dari tar, menerima 29 GB file pbrt dan 38 GB peta tekstur ptex 2 . Saya mencoba membuat adegan di sistem rumah saya (dengan 16 GB RAM dan 4-core CPU). Setelah kembali ke komputer setelah beberapa waktu, saya melihat bahwa itu dibekukan, semua RAM penuh, dan pbrt masih berusaha untuk menyelesaikan uraian adegan deskripsi. OS berusaha untuk mengatasi tugas menggunakan memori virtual, tetapi tampaknya sia-sia. Setelah mengalahkan prosesnya, saya harus menunggu sekitar satu menit sebelum sistem mulai merespons tindakan saya.

Upaya berikutnya adalah turunan dari Google Compute Engine, yang memungkinkan Anda untuk menggunakan lebih banyak RAM (120 GB) dan lebih banyak CPU (32 utas pada 16 CPU). Berita baiknya adalah pbrt berhasil membuat adegan (berkat kerja Heather dan Rasmus untuk mengubahnya menjadi format pbrt). Sangat menarik untuk melihat bahwa pbrt dapat menghasilkan piksel yang relatif baik untuk konten film berkualitas tinggi, tetapi kecepatannya ternyata tidak begitu luar biasa: 34 menit 58 detik hanya untuk menguraikan deskripsi adegan, dan selama rendering sistem menghabiskan hingga 70 GB RAM.

Ya, ada 29 gigabyte file deskripsi format format pbrt pada disk yang perlu di-sparsing, jadi saya tidak berharap tahap pertama akan memakan waktu beberapa detik. Tapi habiskan setengah jam bahkan sebelum sinar mulai melacak? Ini sangat menyulitkan pekerjaan dengan adegan itu.

Di sisi lain, kecepatan ini memberi tahu kami bahwa sesuatu yang sangat berbau busuk mungkin terjadi dalam kode; bukan hanya "inversi matriks dapat dilakukan 10% lebih cepat"; lebih tepatnya, sesuatu pada level "oh, kita akan melalui daftar 100 ribu elemen yang terhubung". Saya optimis dan berharap setelah menemukan jawabannya, saya dapat mempercepat proses secara signifikan.

Statistik tidak membantu


Tempat pertama saya mulai mencari petunjuk adalah statistik pbrt dump setelah rendering. Tahap utama dari eksekusi pbrt telah dikonfigurasi sehingga Anda dapat mengumpulkan perkiraan data profil dengan memperbaiki operasi dengan gangguan berkala selama proses rendering. Sayangnya, statistik tidak banyak membantu kami: menurut laporan, dari hampir 35 menit sebelum dimulainya rendering, 4 menit 22 detik dihabiskan untuk membangun BVH, tetapi tidak ada rincian yang diberikan mengenai sisa waktu tersebut.

Membangun BVH adalah satu-satunya tugas komputasi signifikan yang dilakukan selama adegan parsing; yang lainnya pada dasarnya adalah deserialisasi dari deskripsi geometri dan material. Mengetahui berapa banyak waktu yang dihabiskan untuk membuat BVH memberikan pemahaman tentang bagaimana (tidak) efektif sistem adalah: waktu yang tersisa, yaitu sekitar 30 menit, parsing data 29 GB, yaitu, kecepatannya 16,5 MB / s. Parser JSON yang dioptimalkan dengan baik, yang pada dasarnya melakukan tugas yang sama, beroperasi pada kecepatan 50-200 MB / s. Jelas, masih ada ruang untuk perbaikan.

Untuk lebih memahami waktu yang terbuang, saya meluncurkan pbrt dengan alat perf Linux yang belum pernah saya gunakan sebelumnya. Tapi, sepertinya, dia mengatasi tugas itu. Saya menginstruksikan dia untuk mencari karakter DWARF untuk mendapatkan nama fungsi ( --call-graph dwarf ), dan agar tidak mendapatkan 100 GB file jejak, saya harus menurunkan laju pengambilan sampel dari 4000 menjadi 100 sampel per detik ( -F 100 ). Tetapi dengan parameter ini semuanya berjalan dengan baik, dan saya terkejut bahwa alat perf report memiliki antarmuka dengan kutukan yang bagus.

Inilah yang bisa dia katakan kepada saya setelah memulai dengan pbrt:


Saya tidak bercanda ketika saya berbicara tentang "antarmuka dengan kutukan yang bagus".

Kita melihat bahwa lebih dari separuh waktu dihabiskan untuk mekanika parsing: yyparse() adalah yyparse() dihasilkan oleh bison , dan yylex() adalah penganalisa leksikal (lexer) yang dihasilkan oleh flex . Lebih dari separuh waktu dalam yylex() dihabiskan untuk strtod() , yang mengubah string menjadi nilai ganda. Kami akan yyparse() serangan pada yyparse() dan yylex() hingga artikel ketiga dalam seri ini, tetapi sekarang kita sudah dapat memahami bahwa mungkin ada baiknya untuk mengurangi jumlah data yang dilemparkan ke renderer.

Dari teks ke PLY


Salah satu cara untuk mengurangi waktu penguraian data teks adalah dengan mengonversi data ke format yang diurai lebih efisien. Sebagian besar 29 GB file deskripsi adegan ini adalah jerat segitiga, dan pbrt sudah memiliki dukungan asli untuk format PLY , yang merupakan representasi biner efektif dari jerat poligon. Juga di pbrt ada flag baris perintah --toply , yang mem-parsing file deskripsi adegan pbrt, mengubah semua jerat segitiga yang ditemukan menjadi file PLY dan membuat file pbrt baru yang merujuk ke file-file PLY ini.

Tangkapannya adalah bahwa tekstur ptex secara aktif digunakan dalam adegan Disney, yang, pada gilirannya, membutuhkan nilai faceIndex untuk dikaitkan dengan setiap segitiga, yang menentukan wajah sub mesh asli yang diambil. Untuk mentransfer nilai-nilai ini, cukup menambahkan dukungan untuk bidang baru di file PLY . Penelitian lebih lanjut mengungkapkan bahwa dalam kasus mengkonversi setiap mesh - bahkan jika hanya memiliki selusin segitiga - menjadi file PLY, puluhan ribu file PLY kecil dibuat dalam folder, dan ini menciptakan masalah kinerjanya sendiri; Kami berhasil menyingkirkan masalah ini dengan menambahkan pada implementasi kemampuan untuk membiarkan jerat kecil tidak berubah .

Saya menulis skrip baris perintah kecil untuk mengonversi semua file *_geometry.pbrt dalam folder untuk menggunakan PLY untuk jerat besar. Perhatikan bahwa ia memiliki asumsi hard-coded tentang jalur yang perlu diubah agar skrip bekerja di tempat lain.

Peningkatan kecepatan pertama


Setelah mengonversi semua jerat besar ke PLY, ukuran deskripsi adegan pada disk menurun dari 29 menjadi 22 GB: 16,9 GB file adegan pbrt dan 5,1 GB file biner PLY. Setelah konversi, total waktu tahap pertama sistem menurun menjadi 27 menit 35 detik, dan penghematan sebesar 7 menit 23 detik, yaitu, kami dipercepat dengan 1,3 kali 3 . Memproses file PLY jauh lebih efisien daripada memproses file teks pbrt: hanya 40 detik waktu startup dihabiskan untuk mengurai file PLY, dan kami melihat bahwa file PLY diproses dengan kecepatan sekitar 130 MB / s, atau sekitar 8 kali lebih cepat daripada format teks pbrt .

Itu adalah kemenangan mudah yang bagus, tetapi kami masih harus melakukan banyak hal.

Lain kali, kami akan mencari tahu di mana semua memori sebenarnya digunakan, memperbaiki beberapa kesalahan di sini dan mencapai kecepatan lebih dalam proses.

Catatan


  1. Anda sekarang harus memiliki pemahaman yang lebih baik tentang motivasi untuk menambahkan dukungan ptex di pihak saya dan mengubah Disney BSDF ke pbrt tahun lalu.
  2. Semua waktu di sini dan di posting selanjutnya diindikasikan untuk versi WIP (Work In Progress), yang saya pakai sebelum rilis resmi. Tampaknya versi finalnya sedikit lebih besar. Kami akan tetap berpegang pada hasil yang saya rekam ketika bekerja dengan adegan asli, meskipun faktanya mereka tidak cukup sesuai dengan hasil dari versi final. Saya menduga pelajaran dari mereka bisa sama.
  3. Perhatikan bahwa peningkatan kecepatan pada dasarnya adalah apa yang Anda harapkan dengan pengurangan sekitar 50 persen dalam volume data parsing. Jumlah waktu yang kami habiskan menurut profiler mengkonfirmasi ide kami.

Source: https://habr.com/ru/post/id417407/


All Articles