Porting Detroit: Menjadi Manusia dari Playstation 4 ke PC

pengantar



Dalam rangkaian posting ini, kami akan membahas port Detroit: Menjadi Manusia  dari PlayStation 4 ke PC.



Detroit: Being Human dirilis di PlayStation 4 pada Mei 2018. Kami mulai mengerjakan versi PC pada Juli 2018 dan merilisnya pada Desember 2019. Ini adalah game petualangan dengan tiga karakter yang dapat dimainkan dan banyak alur cerita. Ini memiliki grafik berkualitas sangat tinggi dan sebagian besar teknologi grafik dikembangkan oleh Quantic Dream itu sendiri.



Mesin 3D memiliki fitur unggulan:



  • Rendering karakter yang realistis.
  • Pencahayaan PBR.
  • Pasca-pemrosesan berkualitas tinggi seperti Depth of Field (DOF), motion blur, dan sebagainya.
  • Anti-aliasing sementara.






Detroit: Menjadi Manusia



Sejak awal, mesin 3D game ini dirancang khusus untuk PlayStation, dan kami tidak tahu bahwa itu nantinya akan mendukung platform lain. Oleh karena itu, versi PC menjadi tantangan bagi kami.



  • Pimpinan Mesin 3D Ronan Marshalot dan Pimpinan Mesin 3D Nicholas Viseri dan Jonathan Siret dari Quantic Dream akan berbicara tentang aspek rendering dari game yang diangkut. Mereka akan menjelaskan pengoptimalan mana yang dapat dengan mudah dipindahkan dari PlayStation 4 ke PC, dan kesulitan apa yang mereka hadapi karena perbedaan antar platform.
  • Lou Kramer adalah Insinyur Pengembangan Teknologi di AMD . Dia membantu kami mengoptimalkan permainan, jadi dia akan berbicara secara rinci tentang pengindeksan sumber daya yang heterogen di PC dan, khususnya, di kartu AMD.


Memilih API Grafik



Kami sudah memiliki versi OpenGL dari mesin tersebut, yang kami gunakan dalam alat pengembangan kami.



Tetapi kami tidak ingin merilis game di OpenGL:



  • Kami memiliki banyak ekstensi kepemilikan yang tidak terbuka untuk semua produsen GPU.
  • Mesin memiliki kinerja yang sangat rendah di OpenGL, meskipun, tentu saja, dapat dioptimalkan.
  • Di OpenGL, ada banyak cara untuk mengimplementasikan berbagai aspek, jadi penerapan aspek yang berbeda dengan benar di semua platform merupakan mimpi buruk.
  • OpenGL . , , .


Karena banyaknya penggunaan sumber daya yang tidak terkait, kami tidak dapat mem-porting game ke DirectX11. Itu tidak memiliki slot sumber daya yang cukup, dan akan sangat sulit untuk mencapai kinerja yang layak jika kami harus mengulang shader untuk menggunakan lebih sedikit sumber daya.



Kami memilih antara DirectX 12 dan Vulkan, yang memiliki rangkaian fitur yang sangat mirip. Vulkan selanjutnya akan memungkinkan kami memberikan dukungan untuk Linux dan ponsel, dan DirectX 12 akan memberikan dukungan untuk Microsoft Xbox. Kami tahu bahwa pada akhirnya kami perlu mengimplementasikan dukungan untuk kedua API, tetapi akan lebih masuk akal jika port berfokus hanya pada satu API.



Vulkan mendukung Windows 7 dan Windows 8. Karena kami ingin membuat Detroit: Menjadi Manusiadapat diakses oleh sebanyak mungkin pemain, ini telah menjadi argumen yang sangat kuat. Namun, porting membutuhkan waktu satu tahun, dan argumen ini sudah tidak penting, karena Windows 10 sekarang digunakan secara luas!



Berbagai Konsep API Grafik



OpenGL dan versi DirectX yang lebih lama memiliki model kontrol GPU yang sangat sederhana. API ini mudah dipahami dan sangat cocok untuk pembelajaran. Mereka menginstruksikan pengemudi untuk melakukan banyak pekerjaan yang disembunyikan dari pengembang. Oleh karena itu, akan sangat sulit untuk mengoptimalkan mesin 3D yang berfungsi penuh di dalamnya.



Di sisi lain, PlayStation 4 API sangat ringan dan sangat mirip dengan perangkat keras.



Vulkan ada di antara keduanya. Ia juga memiliki abstraksi karena berjalan pada GPU yang berbeda, tetapi pengembang memiliki kendali lebih. Katakanlah kita memiliki tugas untuk mengimplementasikan manajemen memori atau cache shader. Karena lebih sedikit pekerjaan yang tersisa untuk pengemudi, kami harus melakukannya! Namun, kami mengembangkan proyek di PlayStation, dan karena itu lebih nyaman bagi kami ketika kami dapat mengontrol semuanya.



Kesulitan



CPU PlayStation 4 adalah AMD Jaguar dengan 8 core. Jelas lebih lambat dari perangkat keras PC yang lebih baru; Namun, PlayStation 4 memiliki keunggulan penting, khususnya, akses yang sangat cepat ke perangkat keras. Kami yakin API grafik PlayStation 4 jauh lebih efisien daripada semua API di PC. Dia sangat lugas dan hanya membuang sedikit sumber daya. Ini berarti kita dapat mencapai sejumlah besar panggilan draw per frame. Kami tahu bahwa panggilan imbang tinggi bisa menjadi masalah pada PC yang lebih lambat.



Keuntungan penting lainnya adalah bahwa semua shader di PlayStation 4 dapat dikompilasi terlebih dahulu, yang berarti mereka dimuat hampir secara instan. Pada PC, driver harus mengkompilasi shader pada saat boot: karena banyaknya GPU dan konfigurasi driver yang didukung, proses ini tidak dapat dilakukan sebelumnya.



Selama pengembangan Detroit: Being Human di PlayStation 4, para seniman mampu menciptakan pohon shader yang unik untuk semua material. Ini menghasilkan jumlah vertex dan pixel shader yang tidak masuk akal, jadi kami tahu dari awal port bahwa ini akan menjadi masalah besar.



Saluran pipa shader



Seperti yang kita ketahui dari mesin OpenGL, mengompilasi shader dapat memakan waktu lama di PC. Selama produksi game, kami membuat cache shader berdasarkan model GPU workstation kami. Menghasilkan cache shader penuh untuk Detroit: Being Human membutuhkan waktu sepanjang malam! Semua karyawan mendapat akses ke cache shader ini di pagi hari. Tetapi permainan masih melambat, karena pengemudi perlu mengubah kode ini menjadi kode assembler asli dari shader GPU.



Ternyata Vulkan menangani masalah ini jauh lebih baik daripada OpenGL.



Pertama, Vulkan tidak secara langsung menggunakan bahasa shader tingkat tinggi seperti HLSL, melainkan menggunakan bahasa shader perantara yang disebut SPIR-V. SPIR-V mempercepat kompilasi shader dan memudahkan pengoptimalan untuk kompiler shader driver. Faktanya, dalam hal kinerja, ini sebanding dengan sistem cache shader OpenGL.



Di Vulkan, shader harus ditautkan ke formulir VkPipeline. Misalnya, VkPipelineAnda dapat membuat dari vertex dan pixel shader. Ini juga berisi informasi status rendering (tes kedalaman, stensil, pencampuran, dll.) Dan membuat format target. Informasi ini penting bagi pengemudi agar dapat mengkompilasi shader seefisien mungkin.



Di OpenGL, mengompilasi shader tidak mengetahui konteks penggunaan shader. Driver perlu menunggu panggilan draw untuk menghasilkan biner GPU, itulah sebabnya panggilan draw pertama dengan shader baru bisa memakan waktu lama di CPU.



Di Vulkan, pipeline VkPipelinemenyediakan konteks penggunaan, sehingga driver memiliki semua informasi yang diperlukan untuk menghasilkan biner GPU, dan panggilan draw pertama tidak membuang resource. Juga, kami dapat memperbarui VkPipelineCachesaat pembuatan VkPipeline.



Awalnya, kami mencoba membuat VkPipelinessaat pertama kali kami membutuhkannya. Hal ini menyebabkan perlambatan yang mirip dengan situasi pada driver OpenGL. Kemudian VkPipelineCachediperbarui, dan pengereman menghilang hingga panggilan imbang berikutnya.



Kemudian kami memperkirakan bahwa kami akan dapat membuat VkPipelinessaat boot, tetapi jika VkPipelineCachetidak relevan, sangat lambat sehingga strategi pemuatan di latar belakang tidak dapat diterapkan.



Pada akhirnya, kami memutuskan untuk menghasilkan semuanya VkPipelineselama peluncuran pertama game. Ini benar-benar menghilangkan masalah pengereman, tetapi sekarang kita dihadapkan pada kesulitan baru: pembuatannya VkPipelineCachemembutuhkan waktu yang sangat lama.



Detroit: Menjadi Manusia berisi sekitar 99.500 VkPipeline! Gim ini menggunakan rendering maju, jadi shader material berisi semua kode pencahayaan. Oleh karena itu, kompilasi setiap shader bisa memakan waktu lama.



Kami mendapatkan beberapa ide untuk mengoptimalkan proses:



  • , SPIR-V.
  • SPIR-V SPIR-V.
  • , CPU 100% VkPipeline.


Selain itu, pengoptimalan penting disarankan oleh Jeff Boltz dari NVIDIA, dan dalam kasus kami, pengoptimalan tersebut ternyata sangat efektif.



Banyak yang VkPipelinesangat mirip. Misalnya, beberapa VkPipelinemungkin memiliki titik sudut dan bayangan piksel yang sama, hanya berbeda dalam beberapa status render, seperti parameter stensil. Dalam hal ini, pengemudi dapat memperlakukannya sebagai satu jalur pipa. Tetapi jika kita membuatnya pada saat yang sama, salah satu utas akan menganggur, menunggu utas lainnya menyelesaikan tugas. Sesuai sifatnya, proses kami mengirimkan semua yang serupa VkPipelinepada waktu yang sama. Untuk mengatasi masalah ini, kami baru saja mengubah urutan sortir VkPipeline. "Klon" ditempatkan di bagian akhir, dan akibatnya, pembuatannya mulai memakan waktu lebih sedikit.



Performa kreasiVkPipelinessangat bervariasi. Secara khusus, ini sangat tergantung pada jumlah utas perangkat keras yang tersedia. Pada AMD Ryzen Threadripper dengan 64 utas perangkat keras, hanya perlu waktu dua menit. Tetapi pada PC yang lemah, proses ini, sayangnya, dapat memakan waktu lebih dari 20 menit.



Yang terakhir itu terlalu lama bagi kami. Sayangnya, satu-satunya cara untuk mengurangi waktu ini adalah dengan mengurangi jumlah peneduh. Kami perlu mengubah cara kami membuat materi sehingga sebanyak mungkin materi dapat dibagikan. Untuk Detroit: Menjadi Manusia, ini tidak mungkin karena artis harus mengulang semua materi. Kami berencana untuk menerapkan instancing material yang tepat di game berikutnya, tetapi sudah terlambat untuk Detroit: Menjadi Manusia .



Deskriptor pengindeksan



Untuk mengoptimalkan kecepatan panggilan draw di PC, kami menggunakan pengindeksan deskriptor menggunakan ekstensi VK_EXT_descriptor_indexing. Prinsipnya sederhana: kita dapat membuat satu set deskriptor yang berisi semua buffer dan tekstur yang digunakan dalam bingkai. Kemudian kita dapat mengakses buffer dan tekstur melalui indeks. Keuntungan utama dari hal ini adalah resource hanya terikat sekali per frame, meskipun digunakan dalam beberapa panggilan draw. Ini sangat mirip dengan menggunakan sumber daya tak terikat di OpenGL.



Kami membuat array sumber daya untuk semua jenis sumber daya yang digunakan:



  • Satu larik untuk semua tekstur 2D.
  • Satu larik untuk semua tekstur 3D.
  • Satu larik untuk semua tekstur kubik.
  • Satu larik untuk semua penyangga material.


Kami hanya memiliki buffer utama yang berubah di antara panggilan gambar (diimplementasikan sebagai buffer melingkar) yang berisi indeks deskriptor yang merujuk ke buffer material yang diinginkan dan matriks yang diperlukan. Setiap bahan penyangga berisi indeks tekstur yang digunakan.





Berkat strategi ini, kami dapat mempertahankan sejumlah kecil kumpulan deskriptor yang umum untuk semua panggilan gambar dan berisi semua informasi yang diperlukan untuk menggambar bingkai.



Mengoptimalkan pembaruan set deskriptor



Bahkan dengan sejumlah kecil set deskriptor, memperbaruinya masih menjadi hambatan. Memperbarui kumpulan deskriptor bisa sangat mahal jika berisi banyak resource. Misalnya, dalam satu bingkai Detroit: Being Human , bisa ada lebih dari empat ribu tekstur.



Kami telah menerapkan pembaruan tambahan pada kumpulan deskriptor, melacak sumber daya yang menjadi terlihat dan tidak terlihat dalam bingkai saat ini. Selain itu, ini membatasi ukuran larik deskriptor, karena memiliki kapasitas yang cukup untuk menangani sumber daya yang terlihat pada saat ini. Pelacakan visibilitas hanya membuang sedikit sumber daya karena kami tidak menggunakan algoritme yang mahal untuk menghitung persimpangan denganO(n.log(n))... Sebagai gantinya, kami menggunakan dua daftar, satu untuk bingkai saat ini dan satu lagi untuk yang sebelumnya. Memindahkan sumber daya yang terlihat yang tersisa dari satu daftar ke daftar lainnya dan memeriksa sumber daya yang tersisa di daftar pertama membantu untuk menentukan sumber daya mana yang masuk dan menghilang dari piramida visibilitas.



Delta yang diperoleh selama penghitungan ini disimpan untuk empat bingkai - kami menggunakan buffering tiga kali lipat, dan untuk menghitung vektor gerakan objek dengan skinning, diperlukan satu bingkai lagi. Kumpulan deskriptor harus tetap tidak berubah setidaknya selama empat frame sebelum dapat dimodifikasi lagi, karena masih bisa berguna untuk GPU. Oleh karena itu, kami menerapkan delta ke grup empat bingkai.



Pada akhirnya, pengoptimalan ini mengurangi waktu pembaruan untuk kumpulan deskriptor sebesar satu hingga dua kali lipat.



Membantai kaum primitif



Menggunakan pengindeksan deskriptor memungkinkan kita mengelompokkan beberapa primitif dalam satu panggilan draw menggunakan vkCmdDrawIndexedIndirect. Kami menggunakan gl_InstanceIDuntuk mengakses indeks yang diinginkan di buffer utama. Primitif dapat dikelompokkan ke dalam batch jika mereka memiliki kumpulan deskriptor yang sama, pipeline shader yang sama, dan buffer vertex yang sama. Ini sangat efektif, terutama selama lintasan kedalaman dan bayangan. Jumlah total panggilan undian berkurang 60%.



Ini menyimpulkan bagian pertama dari seri artikel. Di Bagian 2, Insinyur Teknologi Lou Kramer akan berbicara tentang pengindeksan sumber daya yang heterogen pada PC dan kartu AMD pada khususnya.



All Articles