Cara meningkatkan kinerja batching

9

Saya sedang mengembangkan game 2D berbasis sprite untuk platform mobile dan saya menggunakan OpenGL (well, sebenarnya Irrlicht) untuk membuat grafik. Pertama saya mengimplementasikan rendering sprite dengan cara sederhana: setiap objek game dirender sebagai quad dengan panggilan draw GPU-nya sendiri, artinya jika saya memiliki 200 objek game, saya membuat 200 draw draw per frame. Tentu saja ini adalah pilihan yang buruk dan permainan saya benar-benar terikat CPU karena ada sedikit overhead CPU yang dikaitkan dalam setiap panggilan draw GPU. GPU sebagian besar tidak digunakan.

Sekarang, saya pikir saya bisa meningkatkan kinerja dengan mengumpulkan benda-benda ke dalam kelompok besar dan membuat kelompok ini hanya dengan beberapa panggilan menarik. Saya menerapkan batching (sehingga setiap objek game yang berbagi tekstur yang sama diberikan dalam batch yang sama) dan berpikir bahwa masalah saya hilang ... hanya untuk mengetahui bahwa frame rate saya bahkan lebih rendah dari sebelumnya.

Mengapa? Yah, saya punya 200 (atau lebih) objek game, dan mereka diperbarui 60 kali per detik. Setiap frame saya harus menghitung ulang posisi baru (terjemahan dan rotasi) untuk simpul dalam CPU (GPU pada platform seluler tidak mendukung instancing jadi saya tidak bisa melakukannya di sana), dan melakukan perhitungan ini 48000 per detik (200 * 60 * 4 sejak setiap sprite memiliki 4 simpul) sepertinya terlalu lambat.

Apa yang bisa saya lakukan untuk meningkatkan kinerja? Semua objek game bergerak / berputar (hampir) setiap frame jadi saya benar-benar harus menghitung ulang posisi vertex. Hanya optimasi yang dapat saya pikirkan adalah tabel pencarian untuk rotasi sehingga saya tidak perlu menghitungnya. Apakah sprite titik membantu? Adakah hack jahat? Ada yang lain?

Terima kasih.

pengguna4241
sumber

Jawaban:

5

Apakah Anda menggunakan port irrlicht saya untuk android? Untuk sprite 2d di Android dan iphone, saya menggunakan trik yang sama seperti Anda: batching. Saya mencoba banyak solusi di OpenGL ES 1.x dan 2.x:

  • urutkan berdasarkan z (paralaks) dan menurut tekstur, lakukan transformasi pada CPU dan panggil glDrawArrays atau glDrawElements (cara tercepat). Gunakan satu tekstur besar jika Anda bisa.
  • Trik yang sama dengan VBO, tidak lebih cepat karena untuk setiap frame Anda menyegarkan semua informasi. Ini dapat berguna untuk sprite statika.
  • gunakan OpenGL ES 2.x dan gunakan Vertex shader untuk menghitung posisi (lebih lambat)
  • gunakan PointSprites (tidak ada solusi jika itu bukan persegi dan terlalu banyak piksel transparan membunuh fillrate)
  • gunakan ekstensi gldrawtexoes ...
  • gunakan drawcall untuk setiap sprite (metode paling lambat)

Jadi seperti Anda, semua transformasi dilakukan oleh CPU untuk OGLES 1.x atau OGLES 2.x. Jika Anda memiliki instruksi neon, Anda dapat menggunakannya untuk mempercepat perhitungan Anda.

Ps: pada perangkat iphone atau android, saya tidak terbatas pada CPU tetapi mengisi tingkat terbatas. Jadi, sangat penting untuk membatasi penarikan berlebih.

Ellis
sumber
Luar biasa, ini adalah sesuatu yang saya cari. Saya tidak mengetahui port Irrlicht Anda, tetapi versi saya tentang Irrlicht sudah berjalan di iOS. Anda mengatakan Anda tidak terbatas pada CPU - berapa banyak sprite yang Anda gambar? Dan apa framerates Anda, katakanlah, untuk 100 sprite di iPhone? Jika saya memiliki 200 objek, saya akhirnya melakukan 48.000 perhitungan per detik. Poin Anda tentang fillrate bagus.
user4241
Sprite statis (latar belakang) ada di VBO. Saya menggunakan satu VBO per paralaks. Kalau tidak, saya punya 100 hingga 200 sprite di Moblox. Di semua iPhone termasuk 3G, saya punya lebih dari 30fps (seingat saya). Tapi sprite besar sangat mahal (mengisi masalah) ....
Ellis
Saya sedang mengerjakan mesin partikel, yang dapat saya gunakan hingga 20.000 partikel dengan semua posisi komputasi dilakukan pada CPU dan saya memiliki 10fps dengan pengaturan ekstrim (pada 3GS dan iPhone4). Jadi 1000 sprite harus dimungkinkan pada 3GS atau iPhone4 dengan framerate yang baik.
Ellis
Terima kasih, sangat membantu! Bagaimana Anda menerapkan mesin partikel Anda? Saya kira Anda bermain-main dengan shader?
user4241
Saya menggunakan shader karena saya perlu gl_PointSize untuk mengatur setiap ukuran partikel. Saya tidak bekerja lagi dengan OGLES 1.x karena ponsel lama bukan target saya. Pertama, semua kode saya adalah OGLES 1.x, kemudian OGLES 1.x dan OGLES 2.x (tidak ada peningkatan kinerja) dan sekarang OGLES 2.x (perbaikan rendering).
Ellis
1

Saya akan merekomendasikan memiliki VBO, dengan masing-masing simpul berisi posisi / rotasi dari setiap objek yang diberikan dan batching berdasarkan tekstur seperti yang Anda lakukan. Saya tidak terlalu terbiasa dengan ogl ES, jadi saya tidak yakin versi glsl mana yang didukungnya, tetapi Anda mungkin dapat melakukan batch berdasarkan pada set tekstur, dan menyimpan yang mana dari 4 atau lebih tekstur yang Anda lewati di Anda akan menggunakan bagian dalam simpul. Sprite poin pasti akan meningkatkan kinerja Anda karena akan mengurangi jumlah data yang Anda kirim secara drastis, dan batching tidak boleh menurunkan kinerja jika Anda melakukannya dengan benar. Selain itu, Anda dapat sedikit meningkatkan kinerja dengan menghitung rotasi pada shader dan hanya meneruskan nilai int / float ke params atau di dalam vertex itu sendiri. (Params akan lebih cepat,

sringer
sumber
Terima kasih atas jawaban Anda. Saran Anda tentang melakukan perhitungan rotasi di shader adalah excellet tapi sayangnya saya menggunakan OpenGL ES 1 yang tidak mendukung shader jadi saya terjebak dengan pipa tetap. Saya akan mencoba sprite titik tetapi saya tidak dapat menggunakannya dalam semua kasus karena ada batas atas untuk ukurannya. Saya masih sedikit pesimis tentang VBO, jika saya menghitung ulang posisi setiap vertex setiap frame, bagaimana VBO membantu?
user4241
itu memungkinkan data vertex Anda untuk tetap di gpu, yang mengurangi jumlah data yang harus Anda kirim ke gpu setiap frame. Anda tidak perlu shader untuk mengambil keuntungan dari ini, Anda tidak perlu mengubah data vertex sama sekali, jika Anda memiliki posisi basis (seperti asal) untuk setiap sprite, Anda dapat mengubah matriks dunia dengan itu berubah sebelum memanggil draw. Namun, ini mungkin sulit saat batching. menggunakan fungsi tetap, mungkin akan lebih bermanfaat untuk hanya beralih ke VBO dan drop batching setidaknya untuk saat ini, yang pasti akan memberi Anda dorongan.
sringer
Saya mengerti maksud Anda. Jadi bagaimanapun, Anda tidak berbicara tentang batching tetapi hanya menggunakan satu panggilan draw untuk menggambar satu objek game. Saya pasti akan menguji bagaimana VBO tanpa batch mempengaruhi FPS dalam game saya, tetapi masih 200 panggilan draw per frame terdengar terlalu besar ... tapi saya rasa saya harus hidup dengan itu. Saya akan menerima jawaban Anda jika tidak ada jawaban lain yang muncul.
user4241
1

Anda menyebutkan platform seluler yang tidak memiliki instancing. Tapi, Anda masih memiliki vertex shaders, bukan?

Dalam hal ini, Anda masih bisa melakukan pseudo instancing, yang juga sangat cepat. Buat VBO (GL_STATIC_DRAW) dengan titik sudut (relatif terhadap titik tengah sprite, misalnya -1 / -1, 1 / -1, 1/1, -1/1) dan semua koordinat tekstur yang Anda butuhkan, di dalamnya .
Kemudian atur salah satu atribut vertex generik untuk setiap panggilan draw ke titik tengah sprite, dan gambar dua segitiga dengan batas buffer. Di dalam vertex shader, baca atribut vertex generik dan tambahkan koordinat titik tersebut.

Itu akan menghemat Anda memblokir transfer data untuk setiap sprite dan harus jauh lebih cepat. Jumlah aktual panggilan undian tidak begitu penting, pemblokiran / penghentian di antara keduanya.

dm.skt
sumber
Ini kedengarannya solusi yang bagus untuk OpenGL ES 2.0. Sayangnya saya menggunakan ES 1 yang tidak memiliki shader sama sekali.
user4241
0

Masalahnya terletak pada jumlah data yang Anda kirim ke setiap frame GPU. Cukup buat VBO untuk setiap batch dan isi sekali, lalu terapkan matriks transformasi yang sesuai (via glMultMatrix, atau shader jika Anda menggunakan ES 2.0) saat menggambar batch.

r2d2rigo
sumber
Saya tidak mengerti bagaimana ini membantu ketika saya memiliki 200 objek game terpisah dengan transformasi unik? Menggunakan glMultMatrix akan menerapkan transformasi yang sama untuk semua objek yang bukan yang saya inginkan. Juga, mengirim data ke GPU bukanlah hambatan; jika saya menghapus kinerja transformasi sisi CPU sangat baik.
user4241
Ya, tetapi VBO masih dapat meningkatkan kinerja jika diterapkan dengan benar. Bagaimana Anda merender 200 objek saat ini? Apakah Anda menggunakan glBegin / glEnd?
TheBuzzSaw
1
Saya menggunakan mesin Irrlicht 3D dengan custom scene node jadi saya tidak menggunakan OpenGL secara langsung (tapi saya rasa ini menggunakan glBegin / glEnd sederhana dalam kasus ini). Apakah VBO benar-benar membantu karena saya harus memodifikasi seluruh buffer setiap frame? Juga, ini tidak menyelesaikan masalah mendasar tentang menjadi terikat CPU karena perhitungan transformasi vertex. Tetapi terima kasih atas jawaban Anda!
user4241