Bagaimana kita mengatasi kebutuhan memori video besar dalam game 2D?
Kami sedang mengembangkan game 2D (Factorio) di allegro C / C ++, dan kami menghadapi masalah dengan meningkatnya kebutuhan memori video saat konten game meningkat.
Kami saat ini mengumpulkan semua info tentang gambar yang akan digunakan pertama, memotong semua gambar ini sebanyak mungkin dan mengaturnya menjadi atlas besar sekencang mungkin. Atlas ini disimpan dalam memori video, yang ukurannya tergantung pada keterbatasan sistem; saat ini biasanya 2 gambar hingga 8192x8192, sehingga mereka membutuhkan memori video 256Mb hingga 512Mb.
Sistem ini bekerja sangat baik bagi kami, karena dengan beberapa optimasi kustom dan memisahkan render dan memperbarui utas kami dapat menggambar puluhan ribu gambar di layar dalam 60 fps; kami memiliki banyak objek di layar, dan memungkinkan zoom-out besar adalah persyaratan penting. Karena kami ingin menambahkan lebih banyak, akan ada beberapa masalah dengan persyaratan memori video, sehingga sistem ini tidak mungkin dapat menampung.
Salah satu hal yang ingin kami coba adalah memiliki satu atlas dengan gambar yang paling umum, dan yang kedua sebagai cache. Gambar akan dipindahkan ke sana dari bitmap memori, sesuai permintaan. Ada dua masalah dengan pendekatan ini:
- Gambar dari bitmap memori ke bitmap video sangat lambat, allegro.
- Tidak mungkin untuk bekerja dengan bitmap video selain dari utas utama, di allegro, sehingga secara praktis tidak dapat digunakan.
Berikut adalah beberapa persyaratan tambahan yang kami miliki:
- Gim harus bersifat tekad, sehingga masalah kinerja / waktu pemuatan tidak pernah dapat mengubah status gim.
- Gim ini real-time, dan akan segera menjadi multipemain. Kita perlu menghindari bahkan gagap terkecil di semua biaya.
- Sebagian besar permainan adalah satu dunia terbuka yang berkelanjutan.
Tes terdiri dari menggambar 10.000 sprite dalam satu batch untuk ukuran dari 1x1 hingga 300x300, beberapa kali untuk setiap konfigurasi. Saya melakukan tes pada Nvidia Geforce GTX 760.
- Bitmap video ke gambar bitmap video mengambil 0,1us per sprite, ketika bitmap sumber tidak berubah di antara bitmap individu (varian atlas); ukurannya tidak masalah
- Bitmap video ke gambar bitmap video, sedangkan bitmap sumber dialihkan di antara gambar (varian non atlas), mengambil 0,56us per sprite; ukurannya juga tidak masalah.
- Memori bitmap ke gambar bitmap video benar-benar mencurigakan. Ukuran dari 1x1 hingga 200x200 mengambil 0,3us per bitmap, jadi tidak terlalu lambat. Untuk ukuran yang lebih besar, waktu mulai meningkat secara dramatis, pada 9us untuk 201x201 hingga 3116us untuk 291x291.
Menggunakan atlas meningkatkan kinerja dengan faktor lebih besar dari 5. Jika saya memiliki 10ms untuk rendering, dengan atlas saya dibatasi hingga 100.000 sprite per frame, dan tanpa itu, batas 20.000 sprite. Ini akan bermasalah.
Saya juga mencoba menemukan cara untuk menguji kompresi bitmap dan format bitmap 1bpp untuk bayangan, tetapi saya tidak dapat menemukan cara untuk melakukan ini di allegro.
sumber
Jawaban:
Kami memiliki kasus serupa dengan RTS kami (Remake KaM). Semua unit dan rumah adalah sprite. Kami memiliki 18.000 sprite untuk unit, rumah, dan medan, ditambah 6.000 lainnya untuk warna tim (diterapkan sebagai topeng). Jangka panjang kami juga memiliki sekitar ~ 30 000 karakter yang digunakan dalam font.
Jadi ada beberapa optimisasi terhadap atlas RGBA32 yang Anda gunakan:
Pisahkan kumpulan sprite Anda menjadi banyak atlas yang lebih kecil terlebih dahulu dan gunakan sesuai permintaan sebagaimana tercakup dalam jawaban lain. Itu juga memungkinkan untuk menggunakan teknik optimasi yang berbeda untuk setiap atlas secara individual . Saya menduga Anda akan memiliki sedikit RAM yang terbuang, karena ketika mengemas tekstur sedemikian besar biasanya ada area yang tidak digunakan di bagian bawah;
Cobalah menggunakan tekstur palet . Jika Anda menggunakan shader Anda dapat "menerapkan" palet dalam kode shader;
Anda mungkin ingin menambahkan opsi untuk menggunakan RGB5_A1 alih-alih RGBA8 (jika misalnya bayangan kotak-kotak tidak masalah untuk game Anda). Hindari 8bit Alpha jika memungkinkan dan gunakan format RGB5_A1 atau setara dengan presisi yang lebih kecil (sama seperti RGBA4), mereka mengambil setengah ruang;
Pastikan Anda mengemas sprite dengan ketat menjadi atlas (lihat Algoritma Pengemasan Bin), putar sprite bila perlu dan lihat apakah Anda dapat tumpang tindih sudut transparan untuk spanduk belah ketupat;
Anda dapat mencoba format kompresi perangkat keras (DXT, S3TC, dll.) - mereka dapat secara dramatis mengurangi penggunaan RAM, tetapi memeriksa artefak kompresi - pada beberapa gambar perbedaannya tidak terlalu mencolok (Anda dapat menggunakan ini secara selektif seperti dijelaskan pada poin pertama), tetapi pada beberapa - sangat diucapkan. Format kompresi yang berbeda menyebabkan artefak yang berbeda, sehingga Anda dapat memilih yang terbaik untuk gaya seni Anda.
Lihatlah ke sprite besar yang membelah (tentu saja tidak secara manual, tetapi di dalam pengemas atlas tekstur Anda) menjadi sprite latar belakang statis dan sprite yang lebih kecil untuk bagian animasi.
sumber
Pertama-tama Anda perlu menggunakan lebih banyak, atlas tekstur yang lebih kecil. Semakin sedikit tekstur yang Anda miliki, manajemen memori akan semakin sulit dan kaku. Saya akan menyarankan ukuran atlas 1024, dalam hal ini Anda akan memiliki 128 tekstur bukan 2, atau 2048 dalam hal ini Anda akan memiliki 32 tekstur, yang dapat Anda muat dan bongkar sesuai kebutuhan.
Sebagian besar permainan melakukan manajemen sumber daya ini dengan memiliki batas level, sementara layar pemuatan ditampilkan semua sumber daya yang tidak diperlukan lagi di tingkat berikutnya dibongkar dan sumber daya yang diperlukan dimuat.
Pilihan lain adalah On-Demand loading, yang menjadi perlu jika batas level tidak diinginkan atau bahkan level tunggal terlalu besar untuk masuk ke dalam memori. Dalam hal ini gim akan mencoba memprediksi apa yang akan dilihat pemain di masa depan dan memuatnya di latar belakang. (Misalnya: barang-barang yang saat ini berjarak 2 layar dari pemain.) Pada saat yang sama hal-hal yang tidak digunakan lagi untuk waktu yang lebih lama akan diturunkan.
Namun ada satu masalah, apa yang terjadi ketika sesuatu yang tidak terduga terjadi yang tidak dapat diramalkan oleh game?
sumber
Wow, itu lumayan banyak sprite animasi, dihasilkan dari model 3D saya kira?
Anda seharusnya tidak membuat game ini dalam 2D mentah. Ketika Anda memiliki perspektif tetap, hal lucu terjadi, Anda dapat dengan mulus mencampurkan sprite dan latar yang telah dirender sebelumnya dengan model 3D yang dibuat langsung, yang telah banyak digunakan oleh beberapa game. Jika Anda menginginkan animasi yang begitu bagus, itu sepertinya cara paling alami untuk melakukannya. Dapatkan mesin 3D, konfigurasikan untuk menggunakan perspektif isometrik, dan render objek yang Anda terus gunakan sprite sebagai permukaan datar sederhana dengan gambar di atasnya. Dan Anda dapat menggunakan kompresi tekstur dengan mesin 3D, itu saja merupakan langkah maju yang besar.
Saya tidak berpikir memuat dan membongkar akan banyak membantu Anda karena Anda dapat memiliki hampir semua yang ada di layar pada saat yang sama.
sumber
Pertama-tama temukan format tekstur paling efisien yang Anda dapat sambil tetap senang dengan visual permainan apakah ini RGBA4444, atau kompresi DXT dll. Jika Anda tidak puas dengan artefak yang dihasilkan menjadi gambar yang dikompresi alfa DXT, apakah itu layak membuat gambar non-transparan menggunakan kompresi DXT1 untuk warna yang dikombinasikan dengan tekstur masking abu-abu 4 atau 8 bit untuk alpha? Saya membayangkan Anda akan tetap menggunakan RGBA8888 untuk GUI.
Saya menganjurkan memecah hal-hal menjadi tekstur yang lebih kecil menggunakan format apa pun yang Anda memutuskan. Tentukan item yang selalu ada di layar dan karena itu selalu dimuat, ini mungkin dataran dan GUI atlas. Saya kemudian akan memecah item yang tersisa yang biasanya dirender bersama sebanyak mungkin. Saya tidak membayangkan Anda akan kehilangan terlalu banyak kinerja bahkan hingga 50-100 panggilan imbang pada PC tetapi perbaiki jika saya salah.
Langkah selanjutnya adalah membuat versi mipmap dari tekstur ini seperti yang ditunjukkan seseorang di atas. Saya tidak akan menyimpannya dalam satu file tetapi secara terpisah. Jadi, Anda akan mendapatkan versi 1024x1024, 512x512, 256x256 dll dari setiap file dan saya akan melakukan ini sampai saya mencapai tingkat detail terendah yang ingin saya tampilkan.
Sekarang Anda memiliki tekstur terpisah, Anda dapat membangun sistem level of detail (LOD) yang memuat tekstur untuk tingkat zoom saat ini, dan menurunkan tekstur jika tidak digunakan. Tekstur tidak digunakan jika item yang diberikan tidak ada di layar atau tidak diperlukan oleh level zoom saat ini. Cobalah untuk memuat tekstur ke dalam RAM video dalam utas yang terpisah dengan utas pembaruan / render. Anda dapat menampilkan tekstur LOD terendah hingga yang diperlukan dimuat. Ini kadang-kadang menghasilkan perubahan yang terlihat antara detail rendah / tekstur detail tinggi tapi saya membayangkan ini hanya akan terjadi ketika Anda melakukan zooming yang sangat cepat keluar dan masuk saat bergerak melintasi peta. Anda dapat membuat sistem cerdas dengan mencoba melakukan preload di tempat yang menurut Anda orang tersebut akan bergerak atau memperbesar dan memuat sebanyak mungkin dalam batasan memori saat ini.
Itu adalah hal yang akan saya uji untuk melihat apakah itu membantu. Saya membayangkan untuk mendapatkan tingkat pembesaran yang ekstrem Anda pasti akan membutuhkan sistem LOD.
sumber
Saya percaya bahwa pendekatan terbaik adalah dengan membagi tekstur dalam banyak file dan memuatnya sesuai permintaan. Mungkin masalah Anda adalah Anda mencoba memuat tekstur yang lebih besar yang Anda perlukan untuk adegan 3D yang lengkap dan Anda menggunakan Allegro untuk itu.
Untuk zoom-out besar yang ingin Anda terapkan, Anda harus menggunakan mipmaps. Mipmaps adalah versi resolusi rendah dari tekstur Anda yang digunakan ketika objek cukup jauh dari kamera. Ini berarti bahwa Anda dapat menyimpan 8192x8192 Anda sebagai 4096x4096 dan kemudian 2048x2048 dan seterusnya, dan Anda beralih ke resolusi yang lebih rendah semakin kecil Anda melihat sprite di layar. Anda dapat menyimpan keduanya sebagai tekstur terpisah atau mengubah ukurannya saat memuat (tetapi membuat mipmaps saat runtime akan meningkatkan waktu pemuatan untuk game Anda).
Sistem manajemen yang tepat akan memuat file yang diperlukan sesuai permintaan dan melepaskan sumber daya ketika tidak ada yang menggunakannya, ditambah hal-hal lain. Manajemen sumber daya adalah topik penting dalam pengembangan game dan Anda mereduksi manajemen Anda menjadi pemetaan koordinat sederhana menjadi satu tekstur, yang hampir tidak memiliki manajemen sama sekali.
sumber
Saya sarankan untuk membuat lebih banyak file atlas yang dapat dikompresi dengan zlib dan dialirkan keluar dari kompresi untuk setiap atlas, dan dengan memiliki lebih banyak file atlas dan file ukuran yang lebih kecil Anda dapat menahan jumlah data gambar aktif dalam memori video. Selain itu, terapkan mekanisme triple buffer sehingga Anda menyiapkan masing-masing bingkai gambar lebih cepat dan memiliki kesempatan untuk menyelesaikan lebih cepat sehingga gagap tidak muncul di layar.
sumber