We are currently migrating Bugzilla to GitHub issues.
Any changes made to the bug tracker now will be lost, so please do not post new bugs or make changes to them.
When we're done, all bug URLs will redirect to their equivalent location on the new bug tracker.

Bug 5275

Summary: Allow pause/resume handling from other threads in Android
Product: SDL Reporter: suriya.patches
Component: videoAssignee: Sam Lantinga <slouken>
Status: RESOLVED INVALID QA Contact: Sam Lantinga <slouken>
Severity: API change    
Priority: P2 CC: sylvain.becker
Version: 2.0.12   
Hardware: All   
OS: Android (All)   

Description suriya.patches 2020-08-28 01:01:35 UTC
Although SDL_androidevents.c handles context backup and restore, this only handles the context for the main thread and not contexts in the case that rendering occurs on a different thread. This means that upon pause and resume, the screen ends up going black because the rendering thread context has not been resumed. In my case, I have a video renderer on a thread separate from the main thread and I need to save the context on pause and restore it on resume in this thread. 

My workaround for now was to make the `Android_PauseSem` and `Android_ResumeSem` from SDL_androidvideo.h available in an h file accessible from outside the library and then imitate the behavior of the existing `android_egl_context_restore` and `android_egl_context_backup` in the render loop. This is the code I use to backup and restore in my render thread, within the render loop, with `isPaused`, `isPausing`, and `saved_context` defined outside the loop:

```
#ifdef __ANDROID_API__
        // check if the android app is paused (backgrounded) and store the context
        // when resuming, restore the context to the one that has just been stored
        // this code is derived from that in `SDL2/src/video/android/SDL_androidevents.c`
        // for backup and restore of context
        if (isPaused) {
            saved_context = SDL_GL_GetCurrentContext();
            /* We need to do this so the EGLSurface can be freed */
            SDL_GL_MakeCurrent((SDL_Window*) window, NULL);

            if (SDL_SemWait(Android_ResumeSem) == 0) {
                isPaused = 0;

                SDL_Event event;
                if (SDL_GL_MakeCurrent((SDL_Window*) window, saved_context)) {
                    /* The context is no longer valid, create a new one */
                    SDL_GL_MakeCurrent((SDL_Window*) window, saved_context);
                    event.type = SDL_RENDER_DEVICE_RESET;
                    SDL_PushEvent(&event);
                }
            }
        } else {
            if (isPausing || SDL_SemTryWait(Android_PauseSem) == 0) {
                if (SDL_NumberOfEvents(SDL_APP_DIDENTERBACKGROUND) > SDL_SemValue(Android_PauseSem)) {
                    isPausing = 1;
                } else {
                    isPausing = 0;
                    isPaused = 1;
                }
            }
        }
#endif
```

Polled events don't seem to reach threads apart from the main thread, so I couldn't use `SDL_WINDOWEVENT_FOCUS_LOST` and `SDL_WINDOWEVENT_FOCUS_GAINED` and settled on this implementation instead. It would be great to know if an alternate method exists to accomplish the same thing or, if not, have an API addition that allows any thread to monitor the changes between an Android app pausing, being paused, or being resumed for context backup and restoration. Making `Android_PauseSem` and `Android_ResumeSem` public works, but may not be the cleanest solution?
Comment 1 Sylvain 2020-08-28 08:43:07 UTC
you can monitor isPaused/isPausing, but this isn't really correct:
there can be timing issue. isPaused/isPausing comes late, appear twice, etc.
if you do that, this is for sure no robust : any delay added can break it.

if at the same time, you consume Android_ResumeSem / Android_PauseSem you're going to disturb the main thread.

if you're app is multi-threaded, 
you should synchronize with the main thread and do an handshaking/acknolegement for the pause/resume.

WILL_ENTER_BG tells you that you should stop doing any rendering, and it will backup the context on the next event polled. (which is DID_ENTER_BG).

So, after WILL_ENTER_BG: you need to synchronize/wait with your other thread to acknoledge the pause.
Comment 2 suriya.patches 2020-08-31 17:15:36 UTC
I see... this was very helpful! I ended up communicating the backgrounding status via event polling from the main thread to the rendering thread and then reading the backgrounding status in the rendering thread to decide whether to save the context or make it current. In the end - no changes to the library were necessary, which is reassuring :)
Comment 3 Sylvain 2020-08-31 18:22:41 UTC
ok great, I mark this as fixed !