Android update breaks OpenGL ES 1.0

I’ve no idea whether this is related to SDL or not, but an Android update today has caused my app to stop working, and it appears that calls to OpenGLES (1.0) functions are now resulting in a SEGV_ACCERR fault. SDL_GetRendererInfo confirms that I have successfully created an OpenGLES 1.0 context, so I’m mystified.

Edit: OpenGLES 2.0 is failing the same way.

Richard.

It’s looking as though (for example) SDL_GL_GetProcAddress(“glEnable”) is returning a different address from dlsym (RTLD_DEFAULT, “glEnable”). What could cause that?

Which configuration do you have: ndk version ? ABI ? Device ?

SDL_GL_GetProcAddress(“glEnable”) is in the end a call to dlsym(dll_handle, “glEnable”) (so an issue with: dll_handle vs RTLD_DEFAULT pseudo handle ?)

I recently hit an issue with dlopen(), it failed on device “P8 Lite” of android prelaunch.
at https://hg.libsdl.org/SDL/file/43bba409e6d2/src/core/android/SDL_android.c#l417
( https://bugzilla.libsdl.org/show_bug.cgi?id=4002 )

Despite my app successfully opening an OpenGLES 1.0 context, SDL_GL_GetProcAddress is seemingly returning NULL for any function that is not in GLES 2.0! So for example SDL_GL_GetProcAddress(“glLogicOp”) is returning NULL and similarly for “glLightfv”; but non-zero values are returned for “glEnable” and “glClearColor” etc.

dlsym (RTLD_DEFAULT) is returning non-zero values for “glLogicOp” and “glLightfv” etc. but calling those addresses results in an immediate SEGV_ACCERR. So something very strange is happening with address resolution. Where is SDL getting the handle it passes to dlsym?

The architecture is armv7 and the device is a OnePlus 5 phone running OxygenOS 5.0.1 and Android 8.0.0; the issue started only with the latest OS update which arrived yesterday.

Could it fail loading the GLES library so SDL_GL_GetProcAddress() returned NULL, but provides stubs
(dlsym (RTLD_DEFAULT) returning non-zero) ?

Try to add many logs into “src/video/SDL_egl.c” to see if libraries are loaded.

Could this be a restriction to access platform libraries ?
https://developer.android.com/about/versions/nougat/android-7.0-changes.html#ndk

You’re a genius! There’s a section of code in SDL_egl.c which is disabled on Android because “eglGetProcAddress is busted on Android”. But the link given there shows this bug as ‘fixed’ so I’ve removed the conditional test and allowed it to call eglGetProcAddress. Voilà, everything works again!

So it looks as though SDL_egl.c needs attention. For a start the test for Android either needs to be removed or to be made conditional on the version of Android. Also, the fallback code is evidently broken.

I saw this disabled section of code. It solves your issue but maybe it is an issue elsewhere (at least it was).
It says it is fixed but in which version ? is this part of android version or vendor driver ?

Maybe you could double check you issue to see if :
SDL_GL_GetProcAddress() returned NULL because the dlopen() fails to find the library or because it fails to find the symbol. you can call “dlerror()” to have a string error message also.

Because some symbols returned NULL and others returned an address - and assuming it’s the same library for all GLES functions - then I would conclude that it’s the latter. I’ll do the test if you still think it’s important, but in which source file is the ‘dlopen’ call?

yes, maybe you’re right, only one library seems to be loaded directly.

Maybe it worth double checking, because there are some fallback.
for OpenGL ES 1.0, it loads DEFAULT_OGL_ES, or DEFAULT_OGL_ES_PVR

still in src/video/SDL_egl.c, there is SDL_EGL_LoadLibrary().
It calls SDL_LoadObject() which is a “dlopen()”.

Because there are many calls, you can put printf in SDL_LoadObject() ( src/loadso/dlopen/SDL_sysloadso.c ),
so that you’ll see what gets loaded on your device.

Another thing, you can grab the .so file of the device and do an “objdump -T” to see which symbol it contains.

OK, here are the results when I run my app:

01-10 14:26:54.584 22786 22964 I SDL/APP : sofile = ‘libGLESv2.so’ handle = 0xedd94390
01-10 14:26:54.585 22786 22964 I SDL/APP : sofile = ‘libEGL.so’ handle = 0xedd94010
01-10 14:27:37.651 22786 23108 I SDL/APP : sofile = ‘libGLESv2.so’ handle = 0xedd94390
01-10 14:27:37.651 22786 23108 I SDL/APP : sofile = ‘libEGL.so’ handle = 0xedd94010

I don’t directly know which of these corresponds to SDL_GL_GetProcAddress returning NULL, but should SDL be looking in libGLESv2 at all given that I have created a v1 context?

More diagnostics:

01-10 14:59:26.500 23921 23940 I SDL/APP : sofile = ‘libGLESv2.so’ handle = 0xedd94390
01-10 14:59:26.500 23921 23940 I SDL/APP : sofile = ‘libEGL.so’ handle = 0xedd94010
01-10 14:59:26.512 23921 23940 I SDL/APP : proc = ‘glLogicOp’ handle = 0xedd94390 retval from SDL_LoadFunction = 0x0
01-10 14:59:26.512 23921 23940 I SDL/APP : procname = ‘_glLogicOp’ handle = 0xedd94390 retval from SDL_LoadFunction = 0x0

So SDL_GL_GetProcAddress(“glLogicOp”) is calling SDL_LoadFunction with the handle for ‘libGLESv2.so’, but glLogicOp is a GLES 1 function only! Returning NULL in this case is presumably correct, but unhelpful.

I just tried with a OpenGLES 2.0 app, and it also loads two libraries libGLESv2 and libEGL.so (but 2, not 4 times).

first, with “path =DEFAULT_OGL_ES2”, then at comment “Try loading a EGL symbol, if it does not work try the default library paths”. It seems to be expected, it has two handles: egl_dll_handle and dll_handle …

It loads 4 times on your side because they might be a re-creation of SDL_Window underneath. Maybe it fails to get the 1.0 context ? I agree it would make sense to load libGLESv1.
(maybe double-check with another device where it worked)

For me, it’s loaded twice:
with "path =DEFAULT_OGL_ES2"
at comment “Try loading a EGL symbol, if it does not work try the default library paths”

Maybe as you said, there is an issue with th

OK, I can do that. I looked at the docs for eglGetProcAddress and it specifically states that “Function pointers returned by eglGetProcAddress are independent of the display and the currently bound context and may be used by any context” so that looks to be safe.

OK, on a ‘working device’ SDL is still passing the handle of libGLESv2! The only reason it ‘works’ is that my own app has a fallback of calling dlsym(RTLD_DEFAULT) when SDL_GL_GetProcAddress returns NULL.

01-10 15:21:23.833 4573 4609 I SDL/APP : sofile = ‘libGLESv2.so’ handle = 0xed93b010
01-10 15:21:23.862 4573 4609 I SDL/APP : proc = ‘glLogicOp’ handle = 0xed93b010 retval from SDL_LoadFunction = 0x0

What I conclude from this is that SDL has a bug which is causing it to pass the handle of ‘libGLESv2.so’ even when the app has opened a GLES V1 context. This bug went unnoticed by me because my fallback of calling dlsym(RTLD_DEFAULT) has previously always returned the correct address. As the result of yesterday’s OS update, dlsym is no longer doing that, so the SDL bug now hits me.

Yes, it seems a good explanation.
And why doesn’t it load the v1 library ? can you check that ?

check what happens with “gl_config.profile_mask” and "gl_config.major_version"
in src/video/SDL_egl.c

01-10 16:47:52.173 31655 31674 I SDL/APP : gl_config.profile_mask = 4, gl_config.major_version = 2

These appear to be set in SDL_video.c:

#elif SDL_VIDEO_OPENGL_ES2
_this->gl_config.major_version = 2;
_this->gl_config.minor_version = 0;
_this->gl_config.profile_mask = SDL_GL_CONTEXT_PROFILE_ES;

and as far as I can see SDL_VIDEO_OPENGL_ES2 is set unconditionally in SDL_config_android.h:

/* Enable OpenGL ES */
#define SDL_VIDEO_OPENGL_ES 1
#define SDL_VIDEO_OPENGL_ES2 1
#define SDL_VIDEO_OPENGL_EGL 1
#define SDL_VIDEO_RENDER_OGL_ES 1
#define SDL_VIDEO_RENDER_OGL_ES2 1

So I don’t know how it is supposed to work.

Richard.

if you want a 1.0 context, you have to set those variables I think

SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);

I use a hint (which is surely the correct way if you are happy for SDL to fall back to an alternative if your preferred choice isn’t available):

SDL_SetHint (SDL_HINT_RENDER_DRIVER, “opengles”) ;

But it shouldn’t matter how the 1.0 context is established, SDL_GL_GetProcAddress should use the correct library for the actual rendering context in use. It can’t ever make sense to get function addresses from ‘libGLESv2.so’ when a 1.0 context is in use, or ‘libGLESv1.so’ when a 2.0 context is in use.

Edit: Doesn’t SDL_GL_CONTEXT_MAJOR_VERSION apply to OpenGL rather than to OpenGLES?

I’ve tried to create the opengles 1 renderer on Android.

It loads GLESv2 by default.

Then, it recreates the window (src/render/opengles/SDL_render_gles.c) with correct settings

301 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
302 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR);
303 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR);
305 if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) { …

It could reload libraries in SDL_RecreateWindow() but it doesn’t.
It only checks for a difference on window flags, no attribute flags, to reload libraries.
(“if ((window->flags & SDL_WINDOW_OPENGL) != (flags & SDL_WINDOW_OPENGL))” ).
So you still have the v2 libraries loaded with a openges renderer.

Maybe that’s a bug (or a feature, it allows to use the opengles renderer with different context … )

so you currently really need to call :

SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); 

to have the v1 libs loaded.

Maybe fill a bug about this …

I think we’re losing sight of a key factor: OpenGLES provides an API - eglGetProcAddress - which is guaranteed to return the appropriate function address for the current renderer. If SDL were to call this function, the complications you describe would be unnecessary. It currently does not do so because, apparently, “eglGetProcAddress is busted on Android”.

Checking the link given in the source, this issue was marked as “fixed” in August 2010, more than seven years ago! I would argue that it is inappropriate to continue to block the function’s use on Android unconditionally. At the very least, the version of Android should be tested and the fallback code (which seems to be broken) used only when really necessary.