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 3529

Summary: [OpenGLES] SDL_EGL_UnloadLibrary is not called at all on SDL_Quit
Product: SDL Reporter: kaisyu
Component: videoAssignee: Sam Lantinga <slouken>
Status: RESOLVED FIXED QA Contact: Sam Lantinga <slouken>
Severity: major    
Priority: P2    
Version: HG 2.1   
Hardware: All   
OS: All   

Description kaisyu 2016-12-21 08:25:47 UTC
In case of OpenGLES, the sequences of loading and unloading driver library should be like that:


SDL_Init
  ...
  SDL_GL_LoadLibrary
    SDL_EGL_LoadLibrary
...
SDL_Quit
  ...
  SDL_GL_UnloadLibrary
    SDL_EGL_UnloadLibrary
...


However, according to my test results, the varible '_this->gl_config.driver_loaded' does not allow 'SDL_GL_UnloadLibrary' to call 'SDL_EGL_UnloadLibrary'. I have tested the 'testgles2' sample in SDL 2.0.5 official release source package on my Ubuntu 16.04 Desktop PC. Refer to the following logs:


$ ./testgles2
INFO: SDL_VideoInit_REAL => driver_name:(null)
INFO: SDL_GL_LoadLibrary_REAL => path:(null), _this:0x1c12d20
INFO: SDL_GL_LoadLibrary_REAL 2 => _this->gl_config.driver_loaded:0
INFO: SDL_GL_LoadLibrary_REAL 4
INFO: SDL_GL_LoadLibrary_REAL 5 => retval:0
INFO: SDL_GL_LoadLibrary_REAL 6 => _this->gl_config.driver_loaded:1
INFO: SDL_GL_LoadLibrary_REAL END => retval:0
INFO: SDL_GL_UnloadLibrary_REAL
INFO: SDL_GL_UnloadLibrary_REAL 3 => _this->gl_config.driver_loaded:1
INFO: SDL_GL_UnloadLibrary_REAL 5 => _this->GL_UnloadLibrary:0x7efd41a4d4e0
INFO: SDL_GL_UnloadLibrary_REAL END
INFO: SDL_GL_LoadLibrary_REAL => path:(null), _this:0x1c12d20
INFO: SDL_GL_LoadLibrary_REAL 2 => _this->gl_config.driver_loaded:0
INFO: SDL_GL_LoadLibrary_REAL 4
INFO: SDL_EGL_LoadLibrary => egl_path:(null)
INFO: SDL_EGL_LoadLibrary => _this->gl_config.driver_loaded:1
INFO: SDL_GL_LoadLibrary_REAL 5 => retval:0
INFO: SDL_GL_LoadLibrary_REAL 6 => _this->gl_config.driver_loaded:2
INFO: SDL_GL_LoadLibrary_REAL END => retval:0
INFO: Screen bpp: 24
INFO:
INFO: Vendor     : VMware, Inc.
INFO: Renderer   : Gallium 0.4 on llvmpipe (LLVM 3.8, 256 bits)
INFO: Version    : OpenGL ES 3.0 Mesa 11.2.0
INFO: Extensions : GL_EXT_blend_minmax GL_EXT_multi_draw_arrays GL_EXT_texture_compression_dxt1 GL_EXT_texture_format_BGRA8888 GL_OES_compressed_ETC1_RGB8_texture GL_OES_depth24 GL_OES_element
_index_uint GL_OES_fbo_render_mipmap GL_OES_mapbuffer GL_OES_rgb8_rgba8 GL_OES_standard_derivatives GL_OES_stencil8 GL_OES_texture_3D GL_OES_texture_float GL_OES_texture_float_linear GL_OES_te
xture_half_float GL_OES_texture_half_float_linear GL_OES_texture_npot GL_OES_EGL_image GL_OES_depth_texture GL_OES_packed_depth_stencil GL_EXT_texture_type_2_10_10_10_REV GL_OES_get_program_bi
nary GL_APPLE_texture_max_level GL_EXT_discard_framebuffer GL_EXT_read_format_bgra GL_NV_fbo_color_attachments GL_OES_EGL_image_external GL_OES_EGL_sync GL_OES_vertex_array_object GL_ANGLE_tex
ture_compression_dxt3 GL_ANGLE_texture_compression_dxt5 GL_EXT_texture_rg GL_EXT_unpack_subimage GL_NV_draw_buffers GL_NV_read_buffer GL_NV_read_depth GL_NV_read_depth_stencil GL_NV_read_stenc
il GL_EXT_draw_buffers GL_EXT_map_buffer_range GL_KHR_debug GL_OES_depth_texture_cube_map GL_OES_surfaceless_context GL_EXT_color_buffer_float GL_EXT_separate_shader_objects GL_EXT_shader_inte
ger_mix GL_EXT_draw_elements_base_vertex GL_KHR_context_flush_control GL_OES_draw_elements_base_vertex GL_OES_texture_stencil8 GL_EXT_blend_func_extended
INFO:
INFO: SDL_GL_RED_SIZE: requested 5, got 8
INFO: SDL_GL_GREEN_SIZE: requested 5, got 8
INFO: SDL_GL_BLUE_SIZE: requested 5, got 8
INFO: SDL_GL_DEPTH_SIZE: requested 16, got 16
INFO: 31.38 frames per second
INFO: SDL_GL_UnloadLibrary_REAL
INFO: SDL_GL_UnloadLibrary_REAL 3 => _this->gl_config.driver_loaded:2
INFO: SDL_GL_UnloadLibrary_REAL 4 => _this->gl_config.driver_loaded:1



Here is my modified code for logging:

src/video/SDL_egl.c


void
SDL_EGL_UnloadLibrary(_THIS)
{
    SDL_Log("%s => _this->egl_data:%p", __FUNCTION__, _this->egl_data);

    if (_this->egl_data) {
        SDL_Log("%s 2 => egl_display:%p", __FUNCTION__, _this->egl_data->egl_display);
        if (_this->egl_data->egl_display) {
            _this->egl_data->eglTerminate(_this->egl_data->egl_display);
            _this->egl_data->egl_display = NULL;
        }

        SDL_Log("%s 3 => dll_handle:%p", __FUNCTION__, _this->egl_data->dll_handle);
        if (_this->egl_data->dll_handle) {
            SDL_UnloadObject(_this->egl_data->dll_handle);
            _this->egl_data->dll_handle = NULL;
        }
        SDL_Log("%s 4 => egl_dll_handle:%p", __FUNCTION__, _this->egl_data->egl_dll_handle);
        if (_this->egl_data->egl_dll_handle) {
            SDL_UnloadObject(_this->egl_data->egl_dll_handle);
            _this->egl_data->egl_dll_handle = NULL;
        }

        SDL_Log("%s 5", __FUNCTION__);
        SDL_free(_this->egl_data);
        _this->egl_data = NULL;
    }
    SDL_Log("%s END", __FUNCTION__);
}


int
SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_display)
{
    void *dll_handle = NULL, *egl_dll_handle = NULL; /* The naming is counter intuitive, but hey, I just work here -- Gabriel */
    const char *path = NULL;
#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT
    const char *d3dcompiler;
#endif

    SDL_Log("%s => egl_path:%s", __FUNCTION__, egl_path);

    if (_this->egl_data) {
        return SDL_SetError("OpenGL ES context already created");
    }

    _this->egl_data = (struct SDL_EGL_VideoData *) SDL_calloc(1, sizeof(SDL_EGL_VideoData));
    if (!_this->egl_data) {
        return SDL_OutOfMemory();
    }

#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT
    d3dcompiler = SDL_GetHint(SDL_HINT_VIDEO_WIN_D3DCOMPILER);
    if (!d3dcompiler) {
        if (WIN_IsWindowsVistaOrGreater()) {
            d3dcompiler = "d3dcompiler_46.dll";
        } else {
            d3dcompiler = "d3dcompiler_43.dll";
        }
    }
    if (SDL_strcasecmp(d3dcompiler, "none") != 0) {
        SDL_LoadObject(d3dcompiler);
    }
#endif

    /* A funny thing, loading EGL.so first does not work on the Raspberry, so we load libGL* first */
    path = SDL_getenv("SDL_VIDEO_GL_DRIVER");
    if (path != NULL) {
        egl_dll_handle = SDL_LoadObject(path);
    }

    if (egl_dll_handle == NULL) {
        if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
            if (_this->gl_config.major_version > 1) {
                path = DEFAULT_OGL_ES2;
                egl_dll_handle = SDL_LoadObject(path);
            } else {
                path = DEFAULT_OGL_ES;
                egl_dll_handle = SDL_LoadObject(path);
                if (egl_dll_handle == NULL) {
                    path = DEFAULT_OGL_ES_PVR;
                    egl_dll_handle = SDL_LoadObject(path);
                }
            }
        }
#ifdef DEFAULT_OGL
        else {
            path = DEFAULT_OGL;
            egl_dll_handle = SDL_LoadObject(path);
        }
#endif
    }
    _this->egl_data->egl_dll_handle = egl_dll_handle;

    if (egl_dll_handle == NULL) {
        return SDL_SetError("Could not initialize OpenGL / GLES library");
    }

    /* Loading libGL* in the previous step took care of loading libEGL.so, but we future proof by double checking */
    if (egl_path != NULL) {
        dll_handle = SDL_LoadObject(egl_path);
    }
    /* Try loading a EGL symbol, if it does not work try the default library paths */
    if (dll_handle == NULL || SDL_LoadFunction(dll_handle, "eglChooseConfig") == NULL) {
        if (dll_handle != NULL) {
            SDL_UnloadObject(dll_handle);
        }
        path = SDL_getenv("SDL_VIDEO_EGL_DRIVER");
        if (path == NULL) {
            path = DEFAULT_EGL;
        }
        dll_handle = SDL_LoadObject(path);
        if (dll_handle == NULL || SDL_LoadFunction(dll_handle, "eglChooseConfig") == NULL) {
            if (dll_handle != NULL) {
                SDL_UnloadObject(dll_handle);
            }
            return SDL_SetError("Could not load EGL library");
        }
        SDL_ClearError();
    }

    _this->egl_data->dll_handle = dll_handle;

    /* Load new function pointers */
    LOAD_FUNC(eglGetDisplay);
    LOAD_FUNC(eglInitialize);
    LOAD_FUNC(eglTerminate);
    LOAD_FUNC(eglGetProcAddress);
    LOAD_FUNC(eglChooseConfig);
    LOAD_FUNC(eglGetConfigAttrib);
    LOAD_FUNC(eglCreateContext);
    LOAD_FUNC(eglDestroyContext);
    LOAD_FUNC(eglCreateWindowSurface);
    LOAD_FUNC(eglDestroySurface);
    LOAD_FUNC(eglMakeCurrent);
    LOAD_FUNC(eglSwapBuffers);
    LOAD_FUNC(eglSwapInterval);
    LOAD_FUNC(eglWaitNative);
    LOAD_FUNC(eglWaitGL);
    LOAD_FUNC(eglBindAPI);
    LOAD_FUNC(eglQueryString);

#if !defined(__WINRT__)
    _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display);
    if (!_this->egl_data->egl_display) {
        return SDL_SetError("Could not get EGL display");
    }

    if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
        return SDL_SetError("Could not initialize EGL");
    }
#endif

    /* FIXME: Since 'driver_loaded' will increase in the 'SDL_GL_LoadLibrary' function,
       the following line must be commented out.
     */
    _this->gl_config.driver_loaded = 1;
    SDL_Log("%s => _this->gl_config.driver_loaded:%d", __FUNCTION__, _this->gl_config.driver_loaded);

    if (path) {
        SDL_strlcpy(_this->gl_config.driver_path, path, sizeof(_this->gl_config.driver_path) - 1);
    } else {
        *_this->gl_config.driver_path = '\0';
    }

    return 0;
}


----

src/video/SDL_video.c


SDL_GL_LoadLibrary(const char *path)
{
    int retval;

    SDL_Log("%s => path:%s, _this:%p", __FUNCTION__, path, _this);

    if (!_this) {
        return SDL_UninitializedVideo();
    }

    SDL_Log("%s 2 => _this->gl_config.driver_loaded:%d", __FUNCTION__, _this->gl_config.driver_loaded);
    if (_this->gl_config.driver_loaded) {
        if (path && SDL_strcmp(path, _this->gl_config.driver_path) != 0) {
            SDL_Log("%s ERROR 1", __FUNCTION__);
            return SDL_SetError("OpenGL library already loaded");
        }
        SDL_Log("%s 3", __FUNCTION__);
        retval = 0;
    } else {
        if (!_this->GL_LoadLibrary) {
            SDL_Log("%s ERROR 2", __FUNCTION__);
            return SDL_SetError("No dynamic GL support in video driver");
        }
        SDL_Log("%s 4", __FUNCTION__);
        retval = _this->GL_LoadLibrary(_this, path);
    }
    SDL_Log("%s 5 => retval:%d", __FUNCTION__, retval);
    if (retval == 0) {
        ++_this->gl_config.driver_loaded;
        SDL_Log("%s 6 => _this->gl_config.driver_loaded:%d", __FUNCTION__, _this->gl_config.driver_loaded);
    } else {
        SDL_Log("%s 7", __FUNCTION__);
        if (_this->GL_UnloadLibrary) {
            SDL_Log("%s 8", __FUNCTION__);
            _this->GL_UnloadLibrary(_this);
        }
    }
    SDL_Log("%s END => retval:%d", __FUNCTION__, retval);
    return (retval);
}


void
SDL_GL_UnloadLibrary(void)
{
    SDL_Log("%s", __FUNCTION__);

    if (!_this) {
        SDL_Log("%s 2", __FUNCTION__);
        SDL_UninitializedVideo();
        return;
    }
    SDL_Log("%s 3 => _this->gl_config.driver_loaded:%d", __FUNCTION__, _this->gl_config.driver_loaded);
    if (_this->gl_config.driver_loaded > 0) {
        if (--_this->gl_config.driver_loaded > 0) {
            SDL_Log("%s 4 => _this->gl_config.driver_loaded:%d", __FUNCTION__, _this->gl_config.driver_loaded);
            return;
        }
        SDL_Log("%s 5 => _this->GL_UnloadLibrary:%p", __FUNCTION__, _this->GL_UnloadLibrary);
        if (_this->GL_UnloadLibrary) {
            _this->GL_UnloadLibrary(_this);
        }
    }

    SDL_Log("%s END", __FUNCTION__);
}


----


I guess that it is a kind of old bugs which are not found yet. Just in case if it is intended code, please let me know why.

After commenting out '_this->gl_config.driver_loaded = 1;' line in the 'SDL_EGL_LoadLibrary' function, 'SDL_EGL_UnloadLibrary' is called normally. Refer to the following logs:


$ ./testgles2
INFO: SDL_VideoInit_REAL => driver_name:(null)
INFO: SDL_GL_LoadLibrary_REAL => path:(null), _this:0x1c90d20
INFO: SDL_GL_LoadLibrary_REAL 2 => _this->gl_config.driver_loaded:0
INFO: SDL_GL_LoadLibrary_REAL 4
INFO: SDL_GL_LoadLibrary_REAL 5 => retval:0
INFO: SDL_GL_LoadLibrary_REAL 6 => _this->gl_config.driver_loaded:1
INFO: SDL_GL_LoadLibrary_REAL END => retval:0
INFO: SDL_GL_UnloadLibrary_REAL
INFO: SDL_GL_UnloadLibrary_REAL 3 => _this->gl_config.driver_loaded:1
INFO: SDL_GL_UnloadLibrary_REAL 5 => _this->GL_UnloadLibrary:0x7f486e1ab4e0
INFO: SDL_GL_UnloadLibrary_REAL END
INFO: SDL_GL_LoadLibrary_REAL => path:(null), _this:0x1c90d20
INFO: SDL_GL_LoadLibrary_REAL 2 => _this->gl_config.driver_loaded:0
INFO: SDL_GL_LoadLibrary_REAL 4
INFO: SDL_EGL_LoadLibrary => egl_path:(null)
INFO: SDL_EGL_LoadLibrary => _this->gl_config.driver_loaded:0
INFO: SDL_GL_LoadLibrary_REAL 5 => retval:0
INFO: SDL_GL_LoadLibrary_REAL 6 => _this->gl_config.driver_loaded:1
INFO: SDL_GL_LoadLibrary_REAL END => retval:0
INFO: Screen bpp: 24
INFO:
INFO: Vendor     : VMware, Inc.
INFO: Renderer   : Gallium 0.4 on llvmpipe (LLVM 3.8, 256 bits)
INFO: Version    : OpenGL ES 3.0 Mesa 11.2.0
INFO: Extensions : GL_EXT_blend_minmax GL_EXT_multi_draw_arrays GL_EXT_texture_compression_dxt1 GL_EXT_texture_format_BGRA8888 GL_OES_compressed_ETC1_RGB8_texture GL_OES_depth24 GL_OES_element
_index_uint GL_OES_fbo_render_mipmap GL_OES_mapbuffer GL_OES_rgb8_rgba8 GL_OES_standard_derivatives GL_OES_stencil8 GL_OES_texture_3D GL_OES_texture_float GL_OES_texture_float_linear GL_OES_te
xture_half_float GL_OES_texture_half_float_linear GL_OES_texture_npot GL_OES_EGL_image GL_OES_depth_texture GL_OES_packed_depth_stencil GL_EXT_texture_type_2_10_10_10_REV GL_OES_get_program_bi
nary GL_APPLE_texture_max_level GL_EXT_discard_framebuffer GL_EXT_read_format_bgra GL_NV_fbo_color_attachments GL_OES_EGL_image_external GL_OES_EGL_sync GL_OES_vertex_array_object GL_ANGLE_tex
ture_compression_dxt3 GL_ANGLE_texture_compression_dxt5 GL_EXT_texture_rg GL_EXT_unpack_subimage GL_NV_draw_buffers GL_NV_read_buffer GL_NV_read_depth GL_NV_read_depth_stencil GL_NV_read_stenc
il GL_EXT_draw_buffers GL_EXT_map_buffer_range GL_KHR_debug GL_OES_depth_texture_cube_map GL_OES_surfaceless_context GL_EXT_color_buffer_float GL_EXT_separate_shader_objects GL_EXT_shader_inte
ger_mix GL_EXT_draw_elements_base_vertex GL_KHR_context_flush_control GL_OES_draw_elements_base_vertex GL_OES_texture_stencil8 GL_EXT_blend_func_extended
INFO:
INFO: SDL_GL_RED_SIZE: requested 5, got 8
INFO: SDL_GL_GREEN_SIZE: requested 5, got 8
INFO: SDL_GL_BLUE_SIZE: requested 5, got 8
INFO: SDL_GL_DEPTH_SIZE: requested 16, got 16
INFO: 29.66 frames per second
INFO: SDL_GL_UnloadLibrary_REAL
INFO: SDL_GL_UnloadLibrary_REAL 3 => _this->gl_config.driver_loaded:1
INFO: SDL_GL_UnloadLibrary_REAL 5 => _this->GL_UnloadLibrary:0x7f486e1890e0
INFO: SDL_EGL_UnloadLibrary => _this->egl_data:0x1cdbda0
INFO: SDL_EGL_UnloadLibrary 2 => egl_display:0x1d0be40
INFO: SDL_EGL_UnloadLibrary 3 => dll_handle:0x1d2d910
INFO: SDL_EGL_UnloadLibrary 4 => egl_dll_handle:0x1d2d460
INFO: SDL_EGL_UnloadLibrary 5
INFO: SDL_EGL_UnloadLibrary END
INFO: SDL_GL_UnloadLibrary_REAL END
Comment 1 Sam Lantinga 2017-01-02 03:11:00 UTC
Can you check to make sure this is fixed?
https://hg.libsdl.org/SDL/rev/a32fd6b7412b
Comment 2 kaisyu 2017-01-02 04:40:48 UTC
(In reply to Sam Lantinga from comment #1)
> Can you check to make sure this is fixed?
> https://hg.libsdl.org/SDL/rev/a32fd6b7412b

I've tested 'testgles' and 'testgles2' with the revision a32fd6b7412b on my Ubuntu 16.04 desktop PC and they work perfectly. SDL_EGL_UnloadLibrary is called well all the time.

Thanks.
Comment 3 Sam Lantinga 2017-01-02 05:25:43 UTC
Thanks!