diff --git a/src/video/kmsdrm/SDL_kmsdrmopengles.c b/src/video/kmsdrm/SDL_kmsdrmopengles.c index e9c55c9a9..31204f066 100644 --- a/src/video/kmsdrm/SDL_kmsdrmopengles.c +++ b/src/video/kmsdrm/SDL_kmsdrmopengles.c @@ -82,12 +82,16 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) KMSDRM_FBInfo *fb; KMSDRM_PlaneInfo info = {0}; - /* Recreate the GBM / EGL surfaces if the window has been reconfigured. */ - if (windata->egl_surface_dirty) { - if (KMSDRM_CreateSurfaces(_this, window)) { - return SDL_SetError("Failed to do pending surfaces creation"); - } + /* Get the EGL context, now that SDL_CreateRenderer() has already been called, + and call eglMakeCurrent() on it and the EGL surface. */ +#if SDL_VIDEO_OPENGL_EGL + if (windata->egl_context_pending) { + EGLContext egl_context; + egl_context = (EGLContext)SDL_GL_GetCurrentContext(); + SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context); + windata->egl_context_pending = SDL_FALSE; } +#endif /*************************************************************************/ /* Block for telling KMS to wait for GPU rendering of the current frame */ @@ -210,12 +214,16 @@ KMSDRM_GLES_SwapWindowDB(_THIS, SDL_Window * window) KMSDRM_FBInfo *fb; KMSDRM_PlaneInfo info = {0}; - /* Recreate the GBM / EGL surfaces if the window has been reconfigured. */ - if (windata->egl_surface_dirty) { - if (KMSDRM_CreateSurfaces(_this, window)) { - return SDL_SetError("Failed to do pending surfaces creation"); - } + /* Get the EGL context, now that SDL_CreateRenderer() has already been called, + and call eglMakeCurrent() on it and the EGL surface. */ +#if SDL_VIDEO_OPENGL_EGL + if (windata->egl_context_pending) { + EGLContext egl_context; + egl_context = (EGLContext)SDL_GL_GetCurrentContext(); + SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context); + windata->egl_context_pending = SDL_FALSE; } +#endif /****************************************************************************************************/ /* In double-buffer mode, atomic commit will always be synchronous/blocking (ie: won't return until */ diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c index 7d9245f50..d44129059 100644 --- a/src/video/kmsdrm/SDL_kmsdrmvideo.c +++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c @@ -758,100 +758,20 @@ KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo) be disconnected by the kernel. */ void KMSDRM_DestroySurfaces(_THIS, SDL_Window *window) -{ - SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; - - /* Destroy the GBM surface and buffers. */ - if (windata->bo) { - KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo); - windata->bo = NULL; - } - - if (windata->next_bo) { - KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo); - windata->next_bo = NULL; - } - - /* Destroy the EGL surface. */ -#if SDL_VIDEO_OPENGL_EGL - SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - if (windata->egl_surface != EGL_NO_SURFACE) { - SDL_EGL_DestroySurface(_this, windata->egl_surface); - windata->egl_surface = EGL_NO_SURFACE; - } -#endif - - if (windata->gs) { - KMSDRM_gbm_surface_destroy(windata->gs); - windata->gs = NULL; - } -} - -int -KMSDRM_CreateSurfaces(_THIS, SDL_Window * window) -{ - SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); - SDL_WindowData *windata = (SDL_WindowData *)window->driverdata; - SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; - uint32_t surface_fmt = GBM_FORMAT_ARGB8888; - uint32_t surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; - uint32_t width, height; - -#if SDL_VIDEO_OPENGL_EGL - EGLContext egl_context; - SDL_EGL_SetRequiredVisualId(_this, surface_fmt); - egl_context = (EGLContext)SDL_GL_GetCurrentContext(); -#endif - - /* Destroy the surfaces and buffers before creating the new ones. */ - KMSDRM_DestroySurfaces(_this, window); - - if (window->flags & SDL_WINDOW_FULLSCREEN) { - width = dispdata->mode.hdisplay; - height = dispdata->mode.vdisplay; - } - else { - width = window->w; - height = window->h; - } - - if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, surface_fmt, surface_flags)) { - SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway."); - } - - windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, width, height, surface_fmt, surface_flags); - - if (!windata->gs) { - return SDL_SetError("Could not create GBM surface"); - } - -#if SDL_VIDEO_OPENGL_EGL - windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs); - - if (windata->egl_surface == EGL_NO_SURFACE) { - return SDL_SetError("Could not create EGL window surface"); - } - - SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context); - - windata->egl_surface_dirty = 0; -#endif - - return 0; -} - -void -KMSDRM_DestroyWindow(_THIS, SDL_Window *window) { SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); KMSDRM_PlaneInfo plane_info = {0}; - SDL_VideoData *viddata; - if (!windata) { - return; - } +#if SDL_VIDEO_OPENGL_EGL + EGLContext egl_context; +#endif + + /********************************************************************/ + /* BLOCK 1: protect the PRIMARY PLANE before destroying the buffers */ + /* it's using. */ + /********************************************************************/ + #if AMDGPU_COMPAT /************************************************************************/ /* We can't do the usual CRTC_ID+FB_ID to 0 with AMDGPU, because */ @@ -901,9 +821,110 @@ KMSDRM_DestroyWindow(_THIS, SDL_Window *window) #endif /****************************************************************************/ - /* We can finally destroy the window GBM and EGL surfaces, and GBM buffers, */ - /* now that the buffers are not being used by the PRIMARY PLANE anymore. */ + /* BLOCK 2: We can finally destroy the window GBM and EGL surfaces, and */ + /* GBM buffers now that the buffers are not being used by the PRIMARY PLANE */ + /* anymore. */ /****************************************************************************/ + + /* Destroy the GBM surface and buffers. */ + if (windata->bo) { + KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo); + windata->bo = NULL; + } + + if (windata->next_bo) { + KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo); + windata->next_bo = NULL; + } + + /* Destroy the EGL surface. */ +#if SDL_VIDEO_OPENGL_EGL + /***************************************************************************/ + /* In this eglMakeCurrent() call, we disable the current EGL surface */ + /* because we're going to destroy it, but DON'T disable the EGL context, */ + /* because it won't be enabled again until the programs ask for a pageflip */ + /* so we get to SwapWindow(). */ + /* If we disable the context until then and a program tries to retrieve */ + /* the context version info before calling for a pageflip, the program */ + /* will get wrong info and we will be in trouble. */ + /***************************************************************************/ + egl_context = (EGLContext)SDL_GL_GetCurrentContext(); + SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, egl_context); + + if (windata->egl_surface != EGL_NO_SURFACE) { + SDL_EGL_DestroySurface(_this, windata->egl_surface); + windata->egl_surface = EGL_NO_SURFACE; + } +#endif + + if (windata->gs) { + KMSDRM_gbm_surface_destroy(windata->gs); + windata->gs = NULL; + } +} + +int +KMSDRM_CreateSurfaces(_THIS, SDL_Window * window) +{ + SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); + SDL_WindowData *windata = (SDL_WindowData *)window->driverdata; + SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; + uint32_t surface_fmt = GBM_FORMAT_ARGB8888; + uint32_t surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; + uint32_t width, height; + + /* Destroy the surfaces and buffers before creating the new ones. */ + KMSDRM_DestroySurfaces(_this, window); + + if (window->flags & SDL_WINDOW_FULLSCREEN) { + width = dispdata->mode.hdisplay; + height = dispdata->mode.vdisplay; + } + else { + width = window->w; + height = window->h; + } + + if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, surface_fmt, surface_flags)) { + SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway."); + } + + windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, width, height, surface_fmt, surface_flags); + + if (!windata->gs) { + return SDL_SetError("Could not create GBM surface"); + } + +#if SDL_VIDEO_OPENGL_EGL + /* We can't get the EGL context yet because SDL_CreateRenderer has not been called, + but we need an EGL surface NOW, or GL won't be able to render into any surface + and we won't see the first frame. */ + SDL_EGL_SetRequiredVisualId(_this, surface_fmt); + windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs); + + if (windata->egl_surface == EGL_NO_SURFACE) { + return SDL_SetError("Could not create EGL window surface"); + } + + /* Take note that we're still missing the EGL contex, + so we can get it in SwapWindow, when SDL_CreateRenderer() + has already been called. */ + windata->egl_context_pending = SDL_TRUE; +#endif + + return 0; +} + +void +KMSDRM_DestroyWindow(_THIS, SDL_Window *window) +{ + SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; + SDL_VideoData *viddata; + + if (!windata) { + return; + } + KMSDRM_DestroySurfaces(_this, window); /********************************************/ @@ -957,19 +978,9 @@ KMSDRM_ReconfigureWindow( _THIS, SDL_Window * window) { windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2; } -#if SDL_VIDEO_OPENGL_EGL - /* Can't recreate EGL surfaces right now, need to wait until SwapWindow - so the EGL context is available. That's because SDL_CreateRenderer(), - where the EGL context is created, is always called after SDL_CreateWindow() - since SDL_CreateRenderer() takes a window as parameter. - On window destruction, SDL_DestroyRenderer() is called before SDL_DestroWindow(), - so on SDL_DestroyWindow() the EGL context isn't available anymore. */ - windata->egl_surface_dirty = SDL_TRUE; -#else if (KMSDRM_CreateSurfaces(_this, window)) { return -1; } -#endif return 0; } diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.h b/src/video/kmsdrm/SDL_kmsdrmvideo.h index bb162378f..16bee133f 100644 --- a/src/video/kmsdrm/SDL_kmsdrmvideo.h +++ b/src/video/kmsdrm/SDL_kmsdrmvideo.h @@ -126,7 +126,9 @@ typedef struct SDL_WindowData int32_t output_h; int32_t output_x; - SDL_bool egl_surface_dirty; + /* This is for deferred eglMakeCurrent() call: we can't call it until + the EGL context is available, but we need the EGL surface sooner. */ + SDL_bool egl_context_pending; } SDL_WindowData; @@ -152,7 +154,7 @@ typedef struct KMSDRM_PlaneInfo } KMSDRM_PlaneInfo; /* Helper functions */ -int KMSDRM_CreateSurfaces(_THIS, SDL_Window * window); +int KMSDRM_CreateEGLSurface(_THIS, SDL_Window * window); KMSDRM_FBInfo *KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo); /* Atomic functions that are used from SDL_kmsdrmopengles.c and SDL_kmsdrmmouse.c */