Solusi untuk kehilangan konteks OpenGL ketika Android berhenti?

40

Dokumentasi Android mengatakan:

Ada situasi di mana konteks render EGL akan hilang. Ini biasanya terjadi ketika perangkat bangun setelah tidur. Ketika konteks EGL hilang, semua sumber daya OpenGL (seperti tekstur) yang terkait dengan konteks itu akan dihapus secara otomatis. Agar tetap melakukan rendering dengan benar, renderer harus membuat ulang sumber daya yang hilang yang masih dibutuhkan. Metode onSurfaceCreated (GL10, EGLConfig) adalah tempat yang nyaman untuk melakukan ini.

Tetapi harus memuat ulang semua tekstur dalam konteks OpenGL adalah pengalaman yang menyakitkan dan menyakitkan bagi pengguna ketika masuk kembali ke aplikasi setelah jeda. Saya tahu bahwa "Angry Birds" entah bagaimana menghindari ini, saya sedang mencari saran tentang cara mencapai hal yang sama?

Saya bekerja dengan Android NDK r5 (versi CrystaX.) Saya memang menemukan kemungkinan peretasan ini untuk masalah tetapi saya mencoba untuk menghindari membangun seluruh versi SDK khusus.

Nick Gotch
sumber
tidur dan bangun mengacu pada perangkat jadi sementara pengguna hanya menghentikan game Anda atau beralih proses itu harus kehilangan salah satu konteks EGL.
Ali1S232

Jawaban:

22

Pulau Replika memiliki versi GLSurfaceView yang dimodifikasi yang menangani masalah ini (dan bekerja dengan versi Android sebelumnya). Menurut Chris Pruett :

Pada dasarnya, saya meretas GLSurfaceView asli untuk menyelesaikan masalah yang sangat spesifik: Saya ingin pergi ke berbagai Aktivitas dalam aplikasi saya tanpa membuang semua status OpenGL saya. Perubahan besar adalah memisahkan EGLSurface dari EGLContext, dan membuang yang sebelumnya onPause (), tetapi mempertahankan yang terakhir sampai konteksnya hilang secara eksplisit. Implementasi default GLSurfaceView (yang tidak saya tulis, omong-omong), membuang semua status GL saat aktivitas dijeda, dan memanggil onSurfaceCreated () saat dilanjutkan. Itu berarti bahwa, ketika kotak dialog muncul di permainan saya, menutupnya menimbulkan penundaan karena semua tekstur harus dimuat ulang.

Anda harus menggunakan GLSurfaceView default. Jika Anda harus memiliki fungsi yang sama dengan milik saya, Anda dapat melihat milik saya. Tetapi melakukan apa yang saya lakukan memperlihatkan semua jenis bug driver yang mengerikan di beberapa handset (lihat komentar yang sangat panjang di akhir file itu), dan Anda dapat menghindari semua kekacauan itu hanya dengan menggunakan yang default.

Sunting: Saya baru sadar Anda sudah memposting tautan ke retasan yang serupa. Saya tidak berpikir ada solusi built-in sebelum honeycomb. Pulau Replika adalah gim populer yang bekerja pada banyak perangkat dan Anda mungkin menganggap implementasi dan komentar Chris sangat membantu.

Firas Assaad
sumber
1
Setelah mencoba berbagai pendekatan untuk menghindari hal ini, saya telah memutuskan solusi terbaik adalah hanya dengan mengode ulang aplikasi untuk menciptakan kembali konteksnya. Ini adalah pendekatan yang sia-sia tetapi sepertinya tidak ada kode solusi yang baik.google.com/p/android/issues/detail?id=12774
Nick Gotch
9

Saya ingin menambahkan jawaban lain untuk ini yang diteruskan kepada saya satu atau dua tahun yang lalu oleh Chris Pruett (Pulau Replika, Wind-Up Knight, dll). Ini sangat berguna di sini pada tahun 2013 karena setPreserveEglContextOnPause (true) tampaknya tidak berfungsi pada 4.3. (Saya bisa saja salah tentang itu, tetapi itulah yang terlihat bagi saya saat ini ketika saya memperbarui kode permainan yang terakhir disentuh pada 2011).

Pada dasarnya triknya adalah melepaskan GLSurfaceView Anda dari hierarki tampilan dari onPause () Aktivitas Anda. Karena tidak ada dalam hierarki tampilan pada titik onPause () berjalan, konteksnya tidak pernah dihancurkan.

Jadi onPause () Aktivitas Anda akan terlihat seperti ini:

@Override
public void onPause() {
    view.setVisibility(View.GONE);
    super.onPause();
    ...
}

Dan Anda mengembalikan GLSurfaceView Anda ke hierarki bukan dari onResume () tetapi dari onWindowFocusChanged ():

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus && view.getVisibility() == View.GONE) {
         view.setVisibility(View.VISIBLE);
    }
    ...
}

Perhatikan bahwa Anda tidak pernah memanggil onPause () dan onResume () GLSurfaceView dan ini adalah SDK GLSurfaceView resmi, tidak diperlukan versi alternatif yang diretas.

Ruben Scratton
sumber
Sebenarnya pendekatan ini sepertinya tidak berhasil. Chris Pruett menggunakannya dalam kombinasi dengan Unity, mungkinkah solusi ini tidak bekerja di proyek asli? Tapi hanya tidak memanggil GLView.onPause () tampaknya berhasil !? Apakah ada orang lain yang dapat mengkonfirmasi ini?
sjkm
Itu bekerja untuk saya yang ditargetkan pada Android 2.2 OpenGl ES 2. Tidak yakin bagaimana kinerjanya pada perangkat lain. Saya memiliki ponsel LG G2 D802. Adakah yang tahu kalau ini solusi umum?
Pixel
7

[Kata-kata kasar] Saya menghabiskan banyak waktu untuk masalah ini, saya mencoba banyak solusi berbeda dan tidak ada yang berhasil sampai hari ini, saya pikir ini adalah salah satu keputusan desain yang paling buruk yang pernah saya lihat, tetapi berasal dari tim Android. sangat terkejut. </ kata-kata kasar>

Jadi, solusinya adalah dengan memindahkan anggota eglContext ke atas dan menjadikannya statis (global) sehingga tidak akan hancur, maka Anda hanya perlu memeriksa apakah itu null atau tidak sebelum membuatnya lagi.

Sejauh ini solusi ini tampaknya bekerja untuk kami, dan kami tidak peduli jika rusak pada perangkat 2005.

Stephane Cocquereaumont
sumber
4

Gunakan API honeycomb. Ada opsi untuk mempertahankan konteks OGL Anda. Jika tidak, Anda perlu memuat ulang konteks Anda. Itu tidak sulit atau menyakitkan.

Anda perlu memahami bahwa ada dua kasus (Android 2.1):

  • Jeda Layar: aplikasi Anda selalu yang di depan => retas yang tersedia
  • Jeda Aplikasi: ada aplikasi depan lain => Tidak Ada Solusi

Catatan: GPU Android lama tidak mendukung multi konteks. Jadi konteks terbuka hilang ketika Anda beralih ke aplikasi lain => tidak ada solusi yang tersedia (Anda dapat meretas untuk mempertahankan konteks Anda pada jeda layar).

Catatan 2: Fungsi HoneyComb diaturPreserveEGLContextOnPause

Ellis
sumber
1
Saya memindahkan game C ++ ke Android NDK yang tidak dirancang untuk memuat ulang konteks dengan mudah, jadi setidaknya bagi saya itu agak sulit. Selain kesulitan, memuat ulang tekstur akan menyebabkan penundaan yang tidak ada pada perangkat kami yang lain (iPhone, PSP, DS, dll.) Senang mendengar perbaikan honeycomb ini, tetapi sayangnya kami perlu mendukung 2.1+
Nick Gotch
1
Ini sama sekali tidak menjawab pertanyaannya.
notlesh
1
Jika Anda tidak mengerti, Anda tidak bisa melakukannya.
Ellis
2

Beberapa jawaban di sini masuk akal untuk penggunaan awal OpenGL ES di Android. Perangkat GLES pertama hanya mendukung satu konteks, jadi GLSurfaceView dirancang untuk secara agresif membuang status. Meyakinkan GLSurfaceView untuk melakukan sebaliknya tidak mudah.

Untuk versi Android yang lebih baru (mungkin apa saja yang menggunakan GLES 2.x), jawaban terbaik adalah menggunakan SurfaceView dan lakukan EGL dan manajemen utas Anda sendiri. Anda dapat menemukan beberapa contoh GLES yang digunakan dengan SurfaceView polos di Grafika , termasuk perpustakaan kelas sederhana untuk membuat dan menghancurkan konteks EGL.

Ini masih praktik yang baik untuk menurunkan status ketika aplikasi masuk ke latar belakang, tetapi untuk contoh dari Chris Pruett - di mana aplikasi masih berada di latar depan, tetapi Aktivitas yang menjadi tuan rumah GLSurfaceView telah beralih dari - tidak ada nilai dalam merobek bawah konteksnya.

fadden
sumber