SDL_SetRenderTarget crash in Android

OpenGLES removed? · Issue #6793 · libsdl-org/SDL (github.com)

I think you must be looking at the SDL2 source, if you look at the SDL3 source you’ll see that there is no longer an opengles sub-directory.

I give a try.
with latest SDL2
using opengles 1
setting target to null before entering to BG.
polling event, immediately after received the DID ENTER FG, I set the target texture.
and it never crashes.

Since you add a Delay, this still may be a timing issue that has been fixed. I’ve fixed a lot of synchronization issue when switch fg / bg a while ago.

Are you batching drawing commands? I assume the crash is happening because inside SDL_SetRenderTarget() it is flushing all the queued drawing commands out to OpenGLES before it actually changes the target texture.

So to reproduce what is happening here you must have batching enabled (which it isn’t if you change the backend from the default) and have queued some drawing commands.

	SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles");
	SDL_SetHint("SDL_RENDER_BATCHING", "1");
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);

yes batching is on, and the renderer created is opengles.

also, if it helps, before entering to BG, the frame is fully sent. (eg, calling SDL_RenderPresent)

Are you suggesting that SDL_RenderPresent() should be called in response to the SDL_APP_WILLENTERBACKGROUND event? I’m certainly not doing that, and I wouldn’t have thought it was a good idea (SDL_RenderPresent() can wait up to an entire frame period for the next vSync, by which time the app may be fully in the background).

If that fixes the problem, I would consider it another workaround, not really a solution.

Not sure that SDL_RenderPresent() should be the response to WILL ENTER BG.
I don’t think it would hurt. even if it enter bg, you still have some time to do things.
But as soon as you poll the event DID ENTER BG. the gles context is saved and the app in bg. and the next poll event if blocked until foregrond again.

I naively mentioned that “the frame is fully sent”.
because my app behaves like this:

while ( pollEvent ) {
...
} 

// start frame
setTarget valid

copy Target texture to renderer
setTarget to NULL
Present
//end frame

With the scheme, you don’t even need to handle fg/bg. because the loop blocks itself. it won’t render in background because in background PollEvent is blocked.
And with this pattern, the frame is fully sent

I don’t call SDL_PollEvent(), I call SDL_PeepEvents() (which I don’t think blocks when in the background). I need to do that because I’m filtering the range of events I want to receive (particularly because I make extensive use of User Events to handle communication between my worker thread and my GUI thread).

My app is not typical. It does things that most apps don’t do, or in a different way, but always being careful to adhere to the SDL2 API to the extent to which it is documented. For that reason it is quite ‘testing’ of SDL, and has in the past found things that the standard tests have not.

So I’m not surprised if trying to reproduce the problem is proving difficult. Personally I’d tackle it from the other direction: knowing where and when it crashes (from the debug reports), try to find a flaw in the code which might explain it.

For example, as I’ve said before, is there something required when flushing the batch queue which might not be fully restored to a stable state when the SDL_APP_DIDENTERFOREGROUND event is issued?

For example, as I’ve said before, is there something required when flushing the batch queue which might not be fully restored to a stable state when the SDL_APP_DIDENTERFOREGROUND event is issued?

Not that I know. Setting target to NULL (it calls FlushRenderer underneath in my recent SDL code)
Maybe you want to force the SDL_PresentRender but I don’t know.

do that when you peep the WILL ENTER FG, and then, peep the DID ENTER BG.
because as soon as the DID ENTER BG is removed, the android is in BG.

knowing where and when it crashes

Here, it’s not enough. It interferes with something else.
So you have to find the something else.
The part I know is to comment this block ( SDL/SDL_render.c at main · libsdl-org/SDL · GitHub ) see before.

I call SDL_PeepEvents()
not sure how this works exactly. if it is in the same thread as PumpEvent or not.

if it is not in the same thread, then that may be the reason.

You respond too quickly to DID ENTER FG. and then we restore the context (that would a SDL bug I guess).

here the try is to move the “android_egl_context_restore” before sending the DID ENTER FG.

       SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);

225 #if SDL_VIDEO_OPENGL_EGL
226             /* Restore the GL Context from here, as this operation is thread dependent */
227             if (!isContextExternal && !SDL_HasEvent(SDL_QUIT)) {
228                 SDL_LockMutex(Android_ActivityMutex);
229                 android_egl_context_restore(Android_Window);
230                 SDL_UnlockMutex(Android_ActivityMutex);
231             }

216             SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
217             SDL_SendWindowEvent(And

If it is a bug inside SDL, the problem is likely inside some Android interop code rather than the SDL_Render SetRenderTarget implementation I think. So you should try the latest SDL version just to rule out potential existing fixes.

That definitely sounds like an important change. I am restarting my rendering as soon as I see the SDL_APP_DIDENTERFOREGROUND event in my SDL_AddEventWatch() filter, so because my main loop is not blocked in SDL_PollEvent() that could easily be the cause of the crash.

I am following the guidance in README-ios where it says “When these events are delivered you must handle them in an event callback because the OS may not give you any processing time after the events are delivered”. Even if that’s not essential in Android, it’s obviously important that SDL still works correctly.

With the available information I’m happy to conclude that you have probably found the bug. Presumably you will be applying your fix (if appropriate) to both SDL2 and SDL3, and (in SDL2) to both OpenGLES and OpenGLES2.

but can you try the fix ? it’s about moving the 5 line. from “after send fg event” to “before sending fg event”. ?
if you need a diff, here it is:
(it doesn’t read well)


diff --git a/src/video/android/SDL_androidevents.c b/src/video/android/SDL_androidevents.c
index 2e55f4b3e..ea25ae03c 100644
--- a/src/video/android/SDL_androidevents.c
+++ b/src/video/android/SDL_androidevents.c
@@ -131,12 +131,6 @@ void Android_PumpEvents_Blocking(_THIS)
 
             /* Android_ResumeSem was signaled */
             SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
-            SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
-            SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
-
-            ANDROIDAUDIO_ResumeDevices();
-            openslES_ResumeDevices();
-            aaudio_ResumeDevices();
 
             /* Restore the GL Context from here, as this operation is thread dependent */
 #if SDL_VIDEO_OPENGL_EGL
@@ -147,6 +141,13 @@ void Android_PumpEvents_Blocking(_THIS)
             }
 #endif
 
+            SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
+            SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
+
+            ANDROIDAUDIO_ResumeDevices();
+            openslES_ResumeDevices();
+            aaudio_ResumeDevices();
+
             /* Make sure SW Keyboard is restored when an app becomes foreground */
             if (SDL_IsTextInputActive()) {
                 Android_StartTextInput(_this); /* Only showTextInput */
@@ -213,14 +214,6 @@ void Android_PumpEvents_NonBlocking(_THIS)
 
             /* Android_ResumeSem was signaled */
             SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
-            SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
-            SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
-
-            if (videodata->pauseAudio) {
-                ANDROIDAUDIO_ResumeDevices();
-                openslES_ResumeDevices();
-                aaudio_ResumeDevices();
-            }
 
 #if SDL_VIDEO_OPENGL_EGL
             /* Restore the GL Context from here, as this operation is thread dependent */
@@ -231,6 +224,15 @@ void Android_PumpEvents_NonBlocking(_THIS)
             }
 #endif
 
+            SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
+            SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
+
+            if (videodata->pauseAudio) {
+                ANDROIDAUDIO_ResumeDevices();
+                openslES_ResumeDevices();
+                aaudio_ResumeDevices();
+            }
+
             /* Make sure SW Keyboard is restored when an app becomes foreground */
             if (SDL_IsTextInputActive()) {
                 Android_StartTextInput(_this); /* Only showTextInput */







I’m sorry, it’s not convenient for me to try it because it would involve undoing my delay workaround. And because the crash was only intermittent the time and effort involved in testing it would be more than I am prepared to spend. I don’t want you to think I am being uncooperative, but I have other pressures on my time.

then please give a try when you have some time.
also you could easily create the issue
by adding a SDL_Delay in current code.

Send event background + Delay()

restore gl cxt.

so that you process the event, when the context hasn’t been restored.

I’m starting to question whether the guidance in README-ios, which I follow and in so doing contributed to the crash, is actually correct.

Whilst I can understand the requirement to handle the ENTERBACKGROUND events in a callback, because by the time they are seen by SDL_PeepEvents() it may be ‘too late’, surely the same doesn’t apply to the ENTERFOREGROUND events? If they are handled late it shouldn’t matter at all.

Or am I missing something?

behavior between ios and android maybe have be subtlety different. or imprecise. may have bug revealed when using peepevent vs pollevent.

I see on IOS there are some callbacks SDL/SDL_uikitevents.m at main · libsdl-org/SDL · GitHub
but I don´t know well the IOS backend. I don’t know where are the threads are, and how it blocks while entering background.

on android.
it all on the same thread for sending event / save gl context / polling.
as soon as you poll the did enter background, the next pump event (so poll event) will be blocking.
this is on the same thread. if you want to have more thread on top of that, you have to handle the synchronisation