Make sure the thread is actually paused, and context backep-up, before
SurfaceView is destroyed (eg surfaceDestroyed() actually returns).
Add a timeout when surfaceDestroyed() is called, and check 'backup_done' variable.
It prevents crashes like:
#00 pc 000000000000c0d0 /system/lib64/libutils.so (android::RefBase::incStrong(void const*) const+8)
#01 pc 000000000000c7f4 /vendor/lib64/egl/eglSubDriverAndroid.so (EglAndroidWindowSurface::UpdateBufferList(ANativeWindowBuffer*)+284)
#02 pc 000000000000c390 /vendor/lib64/egl/eglSubDriverAndroid.so (EglAndroidWindowSurface::DequeueBuffer()+240)
#03 pc 000000000000bb10 /vendor/lib64/egl/eglSubDriverAndroid.so (EglAndroidWindowSurface::GetBuffer(EglSubResource*, EglMemoryDesc*)+64)
#04 pc 000000000032732c /vendor/lib64/egl/libGLESv2_adreno.so (EglWindowSurface::UpdateResource(EsxContext*)+116)
#05 pc 0000000000326dd0 /vendor/lib64/egl/libGLESv2_adreno.so (EglWindowSurface::GetResource(EsxContext*, EsxResource**, EsxResource**, int)+56)
#06 pc 00000000002ae484 /vendor/lib64/egl/libGLESv2_adreno.so (EsxContext::AcquireBackBuffer(int)+364)
#07 pc 0000000000249680 /vendor/lib64/egl/libGLESv2_adreno.so (EsxContext::Clear(unsigned int, unsigned int, unsigned int, EsxClearValues*)+1800)
#08 pc 00000000002cb52c /vendor/lib64/egl/libGLESv2_adreno.so (EsxGlApiParamValidate::GlClear(EsxDispatch*, unsigned int)+132)
Improve handling of landscape/portrait orientation. Promote to SCREEN_ORIENTATION_SENSOR_* when needed.
Android window can be somehow resizable.
If SDL_WINDOW_RESIZABLE is set, window size change is allowed, for instance when orientation changes (provided the hint allows it).
Message in the log, when going to background:
"call to OpenGL ES API with no current context (logged once per thread)"
Because of SDL_WINDOWEVENT_MINIMIZED is sent from the Java Activity thread.
It calls SDL_RendererEventWatch(), _WindowEvent() and glFinish() without context.
Solution is to move sending of SDL_WINDOWEVENT_MINIMIZED to the SDL thread.
Aaron Barany
Add SDL_HINT_VIDEO_EXTERNAL_CONTEXT hint to notify SDL that the graphics context is external. This disables the automatic context save/restore behavior on Android and avoids using OpenGL by default when SDL_WINDOW_VUKLAN isn't set.
When the application wishes to manage the OpenGL contexts on Android, this avoids cases where SDL unbinds the context and creates new contexts, which can interfere with the application's operation.
When using Vulkan and Metal renderer implementations, this avoids SDL forcing OpenGL to be enabled on certain platforms. While using the SDL_WINDOW_VULKAN flag can be used to achieve the same thing, it also causes Vulkan to be loaded. If the application uses Vulkan directly, this is not necessary, and fails window creation when using Metal due to Vulkan not being present. (assuming MoltenVK isn't installed)
Dan Ginsburg
I've seen this on several devices including Moto Z running Android 7 and a Snapdragon 845 running Android 9.
What happens is as follows:
SDLActivity.onWindowFocusChanged(true) happens pretty early on, but it's before we've done SDL_CreateWindow and so Android_Window is 0x0 thus this message does not get sent:
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)(
JNIEnv *env, jclass cls, jboolean hasFocus)
{
SDL_LockMutex(Android_ActivityMutex);
if (Android_Window) {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeFocusChanged()");
SDL_SendWindowEvent(Android_Window, (hasFocus ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST), 0, 0);
}
SDL_UnlockMutex(Android_ActivityMutex);
}
When the window does get created, in Android_CreateWindow it does this:
window->flags &= ~SDL_WINDOW_RESIZABLE; /* window is NEVER resizeable */
window->flags &= ~SDL_WINDOW_HIDDEN;
window->flags |= SDL_WINDOW_SHOWN; /* only one window on Android */
window->flags |= SDL_WINDOW_INPUT_FOCUS; /* always has input focus */
/* One window, it always has focus */
SDL_SetMouseFocus(window);
SDL_SetKeyboardFocus(window);
The SDL_SetKeyboardFocus does send an SDL_WINDOWEVENT_FOCUS_GAINED message, but it gets eaten in SDL_SendWindowEvent because we've forced SDL_WINDOW_INPUT_FOCUS beforehand:
case SDL_WINDOWEVENT_FOCUS_GAINED:
if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
return 0;
}
window->flags |= SDL_WINDOW_INPUT_FOCUS;
SDL_OnWindowFocusGained(window);
break;
I can fix the problem if I comment out this line from Android_CreateWindow:
window->flags |= SDL_WINDOW_INPUT_FOCUS; /* always has input focus */
I would propose that as a fix unless there is a reason not to.
java layer runs as if separate mouse and touch was 1,
Use SDL_HINT_MOUSE_TOUCH_EVENTS and SDL_HINT_TOUCH_MOUSE_EVENTS
for generating synthetic touch/mouse events
by checking Android_Window validity
- SDLThread: user application is exiting:
SDL_VideoQuit() and clearing SDL_GetVideoDevice()
- ActivityThread is changing orientation/size
surfaceChanged() > Android_SetScreenResolution() > SDL_GetVideoDevice()
- Separate function into Android_SetScreenResolution() and Android_SendResize(),
formating, and mark Android_DeviceWidth/Heigh as static
- If you call onPause() before CreateWindow(), SDLThread will run in infinite loop in background.
- If you call onPause() between a DestroyWindow() and a new CreateWindow(), semaphores are invalids.
SDLActivity.java: the first resume() starts the SDLThread, don't call
nativeResume() as it would post ResumeSem. And the first pause would
automatically be resumed.
First error could happen if Android_SetWindowFullscreen somehow gets
called between SurfaceDestroyed() and SurfaceCreated()
Second error should not happen has native_window validity is guaranteed.
(It would happens previously with error -19)
It accesses data->native_window, which can be changed by onNativeSurfacedChanged().
Currently, Android_SetWindowFullscreen() may access data->native_window after it
has been released, and before a new reference is acquired.
(can be reproduced by adding some SDL_Delay() in onNativeSurfacedChanged and
Android_SetWindowFullscreen() ).
It may happen to have the sequence pause()/resume()/pause(), before polling
any events.
Before, it ends in 'resumed' state because as the check is greedy.
Now, always increase the Pause semaphore, and stop at each pause.
It ends in 'paused' state.
Related to bug 3250: set up a reconfiguration of SurfaceView holder.
Turn the screen off manually before the app starts
(repro rate is not 100%..)
"Pause" transition will add events:
SDL_WINDOWEVENT_ENTER
SDL_WINDOWEVENT_FOCUS_LOST
SDL_WINDOWEVENT_MINIMIZED
SDL_APP_WILL ENTER BACKGROUND
SDL_APP_DID ENTER BACKGROUND
"Resume" transition will add events:
SDL_APP_WILL ENTER FOREGROUND
SDL_APP_DID ENTER FOREGROUND
SDL_WINDOWEVENT_FOCUS_GAINED
SDL_WINDOWEVENT_RESTORED
If Android application doesn't empty the event loop in between, it enters in
"paused" state when SDL_WINDOWEVENT_RESTORED is fetched.
See bug 3250 for pratical case.
Occurs when application goes to background:
- Java activity is destroying SurfaceView holder and "egl_surface" (in onNativeSurfaceDestroyed())
- While native thread is in Android_GLES_SwapWindow(), prepared to call SDL_EGL_SwapBuffers()
The error is "call to eglSwapBuffers failed, reporting an error of EGL_BAD_SURFACE"
It an be reproduced easily by adding a SDL_Delay(100) at the begining of SDL_EGL_SwapBuffers(),
and putting the application into background.
Use a semaphore to prevent concurrency issues between Java Activity and Native thread code, when using 'Android_Window'.
(Eg. Java sending Touch events, while native code is destroying the main SDL_Window. )
Touch device types include SDL_TOUCH_DEVICE_DIRECT (a touch screen with window-relative coordinates for touches), SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE (a trackpad-style device with absolute device coordinates), and SDL_TOUCH_DEVICE_INDIRECT_RELATIVE (a trackpad-style device with screen cursor-relative coordinates).
Phone screens are an example of a direct device type. Mac trackpads are the indirect-absolute touch device type. The Apple TV remote is an indirect-relative touch device type.