Saya sedang menulis klon Minecraft saya sendiri (juga ditulis di Jawa). Ini bekerja sangat baik sekarang. Dengan jarak menonton 40 meter, saya dapat dengan mudah mencapai 60 FPS di MacBook Pro 8,1 saya. (Intel i5 + Intel HD Graphics 3000). Tetapi jika saya menempatkan jarak menonton di 70 meter, saya hanya mencapai 15-25 FPS. Di Minecraft sungguhan, saya bisa menempatkan jarak menonton jauh (= 256m) tanpa masalah. Jadi pertanyaan saya adalah apa yang harus saya lakukan untuk membuat game saya lebih baik?
Optimasi yang saya terapkan:
- Hanya simpan potongan lokal di memori (tergantung pada jarak pandang pemain)
- Frustum culling (Pertama pada bagian, kemudian pada blok)
- Hanya menggambar wajah balok yang benar-benar terlihat
- Menggunakan daftar per bongkahan yang berisi blok yang terlihat. Bongkahan yang menjadi terlihat akan menambahkan dirinya ke daftar ini. Jika mereka tidak terlihat, mereka secara otomatis dihapus dari daftar ini. Blok menjadi (dalam) terlihat dengan membangun atau menghancurkan blok tetangga.
- Menggunakan daftar per chunk yang berisi blok pembaruan. Mekanisme yang sama dengan daftar blok yang terlihat.
- Gunakan hampir tidak ada
new
pernyataan di dalam loop game. (Game saya berjalan sekitar 20 detik hingga Pengumpul Sampah diminta) - Saya menggunakan daftar panggilan OpenGL saat ini. (
glNewList()
,glEndList()
,glCallList()
) Untuk setiap sisi dari jenis blok.
Saat ini saya bahkan tidak menggunakan sistem pencahayaan apa pun. Saya sudah mendengar tentang VBO. Tapi saya tidak tahu persis apa itu. Namun, saya akan melakukan riset tentang mereka. Akankah mereka meningkatkan kinerja? Sebelum menerapkan VBO, saya ingin mencoba menggunakan glCallLists()
dan meneruskan daftar daftar panggilan. Alih-alih menggunakan ribuan kali glCallList()
. (Saya ingin mencoba ini, karena saya pikir MineCraft yang asli tidak menggunakan VBO. Benar?)
Apakah ada trik lain untuk meningkatkan kinerja?
VisualVM profiling menunjukkan kepada saya ini (profil hanya 33 frame, dengan jarak pandang 70 meter):
Pembuatan profil dengan 40 meter (246 bingkai):
Catatan: Saya sedang menyinkronkan banyak metode dan blok kode, karena saya membuat potongan di utas lain. Saya berpikir bahwa mendapatkan kunci untuk suatu objek adalah masalah kinerja ketika melakukan ini dalam satu putaran game (tentu saja, saya berbicara tentang waktu ketika hanya ada loop game dan tidak ada potongan baru yang dihasilkan). Apakah ini benar?
Sunting: Setelah menghapus beberapa synchronised
blok dan beberapa perbaikan kecil lainnya. Performanya sudah jauh lebih baik. Berikut adalah hasil profil baru saya dengan 70 meter:
Saya pikir cukup jelas itulah selectVisibleBlocks
masalahnya di sini.
Terima kasih sebelumnya!
Martijn
Pembaruan : Setelah beberapa perbaikan tambahan (seperti menggunakan untuk loop menggantikan masing-masing, variabel buffering luar loop, dll ...), saya sekarang dapat menjalankan melihat jarak 60 cukup bagus.
Saya pikir saya akan mengimplementasikan VBO sesegera mungkin.
PS: Semua kode sumber tersedia di GitHub:
https://github.com/mcourteaux/CraftMania
sumber
Jawaban:
Anda menyebutkan melakukan pemusnahan frustrasi pada blok individu - cobalah membuangnya. Sebagian besar render potongan harus sepenuhnya terlihat atau seluruhnya tidak terlihat.
Minecraft hanya membangun kembali daftar tampilan / buffer vertex (saya tidak tahu mana yang digunakannya) ketika sebuah blok dimodifikasi dalam suatu potongan, dan begitu juga saya . Jika Anda mengubah daftar tampilan setiap kali tampilan berubah, Anda tidak mendapatkan manfaat dari daftar tampilan.
Selain itu, Anda tampaknya menggunakan bongkahan setinggi dunia. Perhatikan bahwa Minecraft menggunakan potongan kubik 16 × 16 × 16 untuk daftar tampilan, tidak seperti untuk memuat dan menyimpan. Jika Anda melakukan itu, bahkan ada lebih sedikit alasan untuk menyisihkan potongan individu.
(Catatan: Saya belum memeriksa kode Minecraft. Semua informasi ini adalah kabar angin atau kesimpulan saya sendiri dari mengamati rendering Minecraft saat saya bermain.)
Saran yang lebih umum:
Ingat bahwa rendering Anda dijalankan pada dua prosesor: CPU dan GPU. Ketika frame rate Anda tidak mencukupi, maka satu atau yang lain adalah sumber daya yang membatasi - program Anda terikat dengan CPU atau terikat GPU (dengan asumsi itu tidak bertukar atau memiliki masalah penjadwalan).
Jika program Anda berjalan pada 100% CPU (dan tidak memiliki tugas lain tanpa batas untuk diselesaikan), maka CPU Anda melakukan terlalu banyak pekerjaan. Anda harus mencoba menyederhanakan tugasnya (mis. Jangan mengurangi pemusnahan) dengan imbalan GPU lebih banyak. Saya sangat curiga ini adalah masalah Anda, mengingat deskripsi Anda.
Di sisi lain, jika GPU adalah batasnya (sayangnya, biasanya tidak ada monitor beban 0% -100% yang nyaman) maka Anda harus memikirkan cara mengirim data yang lebih sedikit, atau mengharuskannya untuk mengisi lebih sedikit piksel.
sumber
Apa yang memanggil Vec3f.set begitu banyak? Jika Anda sedang membangun apa yang ingin Anda render dari awal setiap frame maka di situlah Anda ingin mulai mempercepatnya. Saya bukan pengguna OpenGL yang banyak dan saya tidak tahu banyak tentang bagaimana membuat Minecraft, tetapi tampaknya fungsi matematika yang Anda gunakan membunuh Anda sekarang (lihat saja berapa banyak waktu yang Anda habiskan di dalamnya dan berapa kali mereka dipanggil - mati dengan seribu luka memanggil mereka).
Idealnya dunia Anda akan disegmentasi sedemikian rupa sehingga Anda dapat mengelompokkan berbagai hal untuk dirender bersama, membangun Objek Penyangga Vertex dan menggunakannya kembali di beberapa bingkai. Anda hanya perlu memodifikasi VBO jika dunia menunjukkan perubahan (seperti yang diedit pengguna). Anda kemudian dapat membuat / memusnahkan VBO untuk apa yang Anda wakili karena hadir dalam rentang yang terlihat untuk menjaga konsumsi memori tetap rendah, Anda hanya akan menerima pukulan karena VBO dibuat daripada setiap frame.
Jika jumlah "doa" benar di profil Anda, Anda sering menelepon banyak hal. (10 juta panggilan ke Vec3f.set ... aduh!)
sumber
Deskripsi saya (dari eksperimen saya sendiri) di sini berlaku:
Untuk rendering voxel, apa yang lebih efisien: VBO pra-dibuat atau shader geometri?
Minecraft dan kode Anda kemungkinan menggunakan pipeline fungsi tetap; upaya saya sendiri telah dengan GLSL tetapi intinya berlaku umum, saya merasa:
(Dari memori) saya membuat frustum yang setengah blok lebih besar dari layar frustum. Saya kemudian menguji titik pusat setiap potongan ( minecraft memiliki 16 * 16 * 128 blok ).
Wajah-wajah di masing-masing memiliki rentang dalam elemen-array VBO (banyak wajah dari potongan berbagi VBO yang sama sampai 'penuh'; berpikir seperti
malloc
; mereka yang memiliki tekstur yang sama di VBO yang sama jika mungkin) dan indeks titik untuk utara wajah, wajah selatan dan sebagainya berbatasan bukan campuran. Ketika saya menggambar, saya melakukan aglDrawRangeElements
untuk wajah utara, dengan yang normal sudah diproyeksikan dan dinormalisasi, dalam seragam. Lalu saya melakukan wajah selatan dan seterusnya, jadi normalnya tidak dalam VBO. Untuk setiap potongan, saya hanya perlu memancarkan wajah-wajah yang akan terlihat - hanya mereka yang berada di tengah layar yang perlu menggambar sisi kiri dan kanan, misalnya; ini sederhanaGL_CULL_FACE
pada tingkat aplikasi.Speedup terbesar, iirc, adalah pemusnahan permukaan interior saat memolimerisasi setiap potongan.
Juga penting adalah manajemen tekstur-atlas dan menyortir wajah dengan tekstur dan menempatkan wajah dengan tekstur yang sama dalam vbo yang sama dengan yang dari potongan lain. Anda ingin menghindari perubahan tekstur terlalu banyak dan menyortir wajah berdasarkan tekstur dan sebagainya meminimalkan jumlah bentang dalam
glDrawRangeElements
. Menggabungkan wajah ubin yang berdekatan menjadi persegi panjang yang lebih besar juga merupakan masalah besar. Saya berbicara tentang menggabungkan jawaban lain yang dikutip di atas.Jelas Anda hanya mempolimerisasi bongkahan yang pernah terlihat, Anda dapat membuang bongkahan yang sudah lama tidak terlihat, dan Anda menyambung kembali bongkahan yang diedit (karena ini adalah kejadian yang jarang terjadi dibandingkan dengan membuatnya).
sumber
Di mana semua perbandingan Anda (
BlockDistanceComparator
) berasal? Jika itu dari fungsi sortir, dapatkah itu diganti dengan radix sort (yang secara asimptotik lebih cepat, dan bukan berbasis perbandingan)?Melihat timing Anda, bahkan jika penyortiran itu sendiri tidak begitu buruk,
relativeToOrigin
fungsi Anda dipanggil dua kali untuk setiapcompare
fungsi; semua data itu harus dihitung sekali. Seharusnya lebih cepat untuk menyortir struktur bantu misalnyadan kemudian di pseudoCode
Maaf jika itu bukan struct Java yang valid (saya belum menyentuh Java sejak undergrad) tapi semoga Anda mendapatkan idenya.
sumber
Ya gunakan VBO dan menghadap CULL, tapi itu berlaku untuk hampir setiap permainan. Yang ingin Anda lakukan hanyalah merender kubus jika terlihat oleh pemain, DAN jika blok bersentuhan dengan cara tertentu (misalkan bongkahan yang tidak dapat Anda lihat karena berada di bawah tanah), Anda menambahkan simpul dari blok dan membuat hampir seperti "blok yang lebih besar", atau dalam kasus Anda - sepotong. Ini disebut serakah serakah dan secara drastis meningkatkan kinerja. Saya mengembangkan game (berbasis voxel) dan menggunakan algoritma serakah yang serakah.
Alih-alih merender semuanya seperti ini:
Ini membuatnya seperti ini:
Kelemahan dari hal ini adalah Anda harus melakukan lebih banyak perhitungan per potongan pada build dunia awal, atau jika pemain menghapus / menambah blok.
hampir semua jenis mesin voxel membutuhkan ini untuk kinerja yang baik.
Apa yang dilakukan adalah memeriksa untuk melihat apakah wajah blok menyentuh wajah blok lain, dan jika demikian: hanya render sebagai satu (atau nol) wajah blok. Ini adalah sentuhan yang mahal ketika Anda membuat potongan sangat cepat.
sumber
Tampaknya kode Anda tenggelam dalam objek dan panggilan fungsi. Mengukur angka, sepertinya tidak ada yang terjadi.
Anda dapat mencoba menemukan lingkungan Java yang berbeda, atau hanya mengacaukan pengaturan yang Anda miliki, tetapi cara sederhana dan sederhana untuk membuat kode Anda, tidak cepat, tetapi banyak yang kurang lambat setidaknya secara internal di Vec3f untuk berhenti coding OOO *. Jadikan setiap metode berisi sendiri, jangan panggil metode lain mana pun hanya untuk melakukan beberapa tugas kasar.
Sunting: Meskipun ada overhead di semua tempat, sepertinya memesan blok sebelum rendering adalah pemakan kinerja terburuk. Apakah itu benar-benar perlu? Jika demikian, Anda mungkin harus memulai dengan melalui loop dan menghitung setiap blok jarak ke asal, dan kemudian urutkan berdasarkan itu.
* Terlalu Berorientasi Objek
sumber
Anda juga dapat mencoba memecah operasi Matematika ke operator bitwise. Jika Anda memiliki
128 / 16
, mencoba untuk membuat operator bitwise:128 << 4
. Ini akan banyak membantu masalah Anda. Jangan mencoba membuat segalanya berjalan dengan kecepatan penuh. Buat pembaruan game Anda pada tingkat 60 atau sesuatu, dan bahkan memecahnya untuk hal-hal lain, tetapi Anda harus melakukan penghancuran dan atau menempatkan voxel atau Anda harus membuat daftar todo, yang akan menurunkan fps Anda. Anda dapat melakukan tingkat pembaruan sekitar 20 untuk entitas. Dan sekitar 10 untuk pembaruan dan atau generasi dunia.sumber