Giải pháp cho việc mất bối cảnh OpenGL khi Android tạm dừng?


40

Tài liệu Android cho biết:

Có những tình huống bối cảnh kết xuất EGL sẽ bị mất. Điều này thường xảy ra khi thiết bị thức dậy sau khi đi ngủ. Khi bối cảnh EGL bị mất, tất cả các tài nguyên OpenGL (như kết cấu) được liên kết với bối cảnh đó sẽ tự động bị xóa. Để giữ kết xuất chính xác, trình kết xuất phải tạo lại bất kỳ tài nguyên bị mất nào mà nó vẫn cần. Phương thức onSurfaceCreated (GL10, EGLConfig) là một nơi thuận tiện để làm điều này.

Nhưng việc phải tải lại tất cả các kết cấu trong ngữ cảnh OpenGL vừa là một nỗi đau và làm tổn thương trải nghiệm trò chơi cho người dùng khi nhập lại ứng dụng sau khi tạm dừng. Tôi biết rằng "Angry Birds" bằng cách nào đó tránh được điều này, tôi đang tìm kiếm gợi ý về cách thực hiện tương tự?

Tôi đang làm việc với Android NDK r5 (phiên bản CrystaX.) Tôi đã tìm thấy sự cố có thể xảy ra với sự cố này nhưng tôi đang cố gắng tránh xây dựng toàn bộ phiên bản SDK tùy chỉnh.


ngủ và thức dậy được giới thiệu đến thiết bị vì vậy trong khi người dùng chỉ cần tạm dừng trò chơi của bạn hoặc chuyển đổi quy trình thì nó sẽ mất bất kỳ bối cảnh EGL nào.
Ali1S 232

Câu trả lời:


22

Bản sao Đảo có phiên bản sửa đổi của GLSurfaceView liên quan đến vấn đề này (và hoạt động với các phiên bản Android trước đó). Theo Chris Pruett :

Về cơ bản, tôi đã hack GLSurfaceView ban đầu để giải quyết một vấn đề rất cụ thể: Tôi muốn đi đến các Hoạt động khác nhau trong ứng dụng của mình mà không phải vứt bỏ tất cả trạng thái OpenGL của mình. Sự thay đổi lớn là tách EGLSurface khỏi EGLContext và vứt bỏ cái cũ trên OnPause (), nhưng giữ lại cái sau cho đến khi bối cảnh bị mất một cách rõ ràng. Việc triển khai mặc định GLSurfaceView (nhân tiện tôi không viết), sẽ loại bỏ tất cả trạng thái GL khi hoạt động bị tạm dừng và gọi onSurfaceCreated () khi nó được tiếp tục. Điều đó có nghĩa là, khi một hộp thoại xuất hiện trong trò chơi của tôi, việc đóng nó phát sinh sự chậm trễ vì tất cả các kết cấu phải được tải lại.

Bạn nên sử dụng GLSurfaceView mặc định. Nếu bạn phải có chức năng tương tự như của tôi, bạn có thể nhìn vào chức năng của tôi. Nhưng làm những gì tôi đã làm lộ ra tất cả các loại lỗi trình điều khiển khủng khiếp trong một số thiết bị cầm tay (xem các bình luận rất dài ở gần cuối tập tin đó) và bạn có thể tránh tất cả sự lộn xộn đó bằng cách chỉ sử dụng một mặc định.

Chỉnh sửa: Tôi mới nhận ra bạn đã đăng liên kết đến một vụ hack tương tự. Tôi không nghĩ có bất kỳ giải pháp tích hợp nào trước tổ ong. Bản sao Đảo là một trò chơi phổ biến hoạt động trên nhiều thiết bị và bạn có thể thấy việc triển khai và nhận xét của Chris hữu ích.


1
Sau khi thử nhiều cách tiếp cận khác nhau để phá vỡ điều này, tôi đã quyết định giải pháp tốt nhất chỉ là mã hóa lại ứng dụng để tạo lại bối cảnh. Đây là một cách tiếp cận lãng phí như vậy nhưng nó không giống như có bất kỳ giải pháp tốt code.google.com/p/android/issues/detail?id=12774
Nick Gotch

9

Tôi muốn thêm một câu trả lời khác cho câu hỏi này đã được Chris Pruett (Đảo bản sao, Hiệp sĩ Wind-Up, v.v.) truyền lại cho tôi một năm. Nó đặc biệt hữu ích ở đây vào năm 2013 vì setPreserveEglContextOnPause (true) dường như không hoạt động trên 4.3. (Tôi có thể sai về điều đó nhưng đó là cách tôi nhìn vào lúc này khi tôi cập nhật mã trò chơi được chạm vào năm 2011).

Về cơ bản, mẹo là tách GLSurfaceView của bạn khỏi hệ thống phân cấp chế độ xem từ onPause () của Activity. Vì nó không nằm trong hệ thống phân cấp khung nhìn tại điểm onPause () chạy, bối cảnh không bao giờ bị phá hủy.

Vì vậy, onPause () của Activity của bạn sẽ giống như thế này:

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

Và bạn khôi phục GLSurfaceView của mình về hệ thống phân cấp không phải từ onResume () mà từ onWindowF FocusChanged ():

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

Lưu ý rằng bạn không bao giờ gọi onSause () và onResume () của GLSurfaceView và đây là SDK GLSurfaceView chính thức, không yêu cầu phiên bản thay thế bị hack.


Trong thực tế phương pháp này dường như không hiệu quả. Chris Pruett đã sử dụng nó kết hợp với Unity, có thể là cách giải quyết này không hoạt động trong các dự án bản địa? Nhưng chỉ không gọi GLView.onPause () dường như hoạt động!? Có những người khác có thể xác nhận điều này?
sjkm

Nó hoạt động với tôi nhắm mục tiêu vào Android 2.2 OpenGl ES 2. Không chắc nó hoạt động như thế nào trên các thiết bị khác. Tôi có một điện thoại LG G2 D802. Có ai biết nếu đây là một giải pháp chung?
Pixel

7

<rant> Tôi đã dành rất nhiều thời gian cho vấn đề này, tôi đã thử nhiều giải pháp khác nhau và không có giải pháp nào cho đến ngày hôm nay, tôi nghĩ đây là một trong những quyết định thiết kế tồi tệ nhất tôi từng thấy, nhưng đến từ nhóm Android tôi không thực sự ngạc nhiên </ rant>

Vì vậy, giải pháp là di chuyển thành viên eglContext lên trên và làm cho nó tĩnh (toàn cầu) để nó không bị phá hủy, sau đó bạn chỉ cần kiểm tra xem nó có rỗng hay không trước khi tạo lại nó.

Cho đến nay, giải pháp này có vẻ hiệu quả với chúng tôi và chúng tôi không quan tâm nếu nó bị hỏng trên các thiết bị năm 2005.


4

Sử dụng API tổ ong. Có một tùy chọn để bảo vệ bối cảnh OGL của bạn. Nếu không, bạn cần tải lại bối cảnh của bạn. Nó không khó cũng không đau.

Bạn cần hiểu rằng có hai trường hợp (Android 2.1):

  • Tạm dừng màn hình: ứng dụng của bạn luôn ở phía trước => hack có sẵn
  • Tạm dừng ứng dụng: có một ứng dụng phía trước khác => Không có giải pháp

Lưu ý: gpus android cũ không hỗ trợ đa ngữ cảnh. Vì vậy, bối cảnh opengl bị mất khi bạn chuyển sang ứng dụng khác => không có giải pháp khả dụng (Bạn có thể hack để bảo vệ bối cảnh của mình khi tạm dừng màn hình).

Lưu ý 2: Hàm HoneyComb được đặtPreserveEGLContextOnPause


1
Tôi đang chuyển một trò chơi C ++ sang Android NDK không được thiết kế để tải lại bối cảnh một cách dễ dàng, vì vậy, đối với tôi, điều đó hơi khó. Những khó khăn sang một bên, tải lại kết cấu sẽ giới thiệu một sự chậm trễ mà không có mặt trên các thiết bị khác của chúng tôi (iPhone, PSP, DS, vv ..) Vui mừng khi nghe các bản sửa lỗi tổ ong này nhưng tiếc là chúng ta cần phải hỗ trợ 2.1 +
Nick Gotch

1
Điều này không trả lời câu hỏi của anh ấy cả.
thịt

1
Nếu bạn không hiểu, bạn không thể làm điều đó.
Ellis

2

Một số câu trả lời ở đây là hợp lý cho việc sử dụng sớm OpenGL ES trên Android. Các thiết bị GLES đầu tiên chỉ hỗ trợ một bối cảnh duy nhất, vì vậy GLSurfaceView được thiết kế để loại bỏ mạnh mẽ trạng thái. Thuyết phục GLSurfaceView để làm khác đi là không dễ dàng.

Đối với các phiên bản Android gần đây hơn (có thể là bất cứ thứ gì sử dụng GLES 2.x), câu trả lời tốt nhất là sử dụng SurfaceView đơn giản và thực hiện quản lý luồng và EGL của riêng bạn. Bạn có thể tìm thấy nhiều ví dụ về GLES được sử dụng với SurfaceView đơn giản trong Grafika , bao gồm một thư viện các lớp đơn giản để tạo và phá hủy bối cảnh EGL.

Vẫn là một cách tốt để dỡ trạng thái khi một ứng dụng chạy vào nền, nhưng ví dụ từ Chris Pruett - nơi ứng dụng vẫn ở phía trước, nhưng Activity lưu trữ GLSurfaceView đã bị loại bỏ - không có giá trị nào trong việc xé xuống bối cảnh.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.