kmsdrm: fix first frame display: no need to wait for SwapWindow() for EGL surface creation.

This commit is contained in:
Manuel Alfayate Corchete 2020-09-06 23:19:54 +02:00
parent 68ac9349aa
commit daa752b10e
3 changed files with 134 additions and 113 deletions

View File

@ -82,12 +82,16 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window)
KMSDRM_FBInfo *fb; KMSDRM_FBInfo *fb;
KMSDRM_PlaneInfo info = {0}; KMSDRM_PlaneInfo info = {0};
/* Recreate the GBM / EGL surfaces if the window has been reconfigured. */ /* Get the EGL context, now that SDL_CreateRenderer() has already been called,
if (windata->egl_surface_dirty) { and call eglMakeCurrent() on it and the EGL surface. */
if (KMSDRM_CreateSurfaces(_this, window)) { #if SDL_VIDEO_OPENGL_EGL
return SDL_SetError("Failed to do pending surfaces creation"); 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 */ /* 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_FBInfo *fb;
KMSDRM_PlaneInfo info = {0}; KMSDRM_PlaneInfo info = {0};
/* Recreate the GBM / EGL surfaces if the window has been reconfigured. */ /* Get the EGL context, now that SDL_CreateRenderer() has already been called,
if (windata->egl_surface_dirty) { and call eglMakeCurrent() on it and the EGL surface. */
if (KMSDRM_CreateSurfaces(_this, window)) { #if SDL_VIDEO_OPENGL_EGL
return SDL_SetError("Failed to do pending surfaces creation"); 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 */ /* In double-buffer mode, atomic commit will always be synchronous/blocking (ie: won't return until */

View File

@ -758,100 +758,20 @@ KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo)
be disconnected by the kernel. */ be disconnected by the kernel. */
void void
KMSDRM_DestroySurfaces(_THIS, SDL_Window *window) 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_WindowData *windata = (SDL_WindowData *) window->driverdata;
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
KMSDRM_PlaneInfo plane_info = {0}; KMSDRM_PlaneInfo plane_info = {0};
SDL_VideoData *viddata; #if SDL_VIDEO_OPENGL_EGL
if (!windata) { EGLContext egl_context;
return; #endif
}
/********************************************************************/
/* BLOCK 1: protect the PRIMARY PLANE before destroying the buffers */
/* it's using. */
/********************************************************************/
#if AMDGPU_COMPAT #if AMDGPU_COMPAT
/************************************************************************/ /************************************************************************/
/* We can't do the usual CRTC_ID+FB_ID to 0 with AMDGPU, because */ /* 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 #endif
/****************************************************************************/ /****************************************************************************/
/* We can finally destroy the window GBM and EGL surfaces, and GBM buffers, */ /* BLOCK 2: We can finally destroy the window GBM and EGL surfaces, and */
/* now that the buffers are not being used by the PRIMARY PLANE anymore. */ /* 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); KMSDRM_DestroySurfaces(_this, window);
/********************************************/ /********************************************/
@ -957,19 +978,9 @@ KMSDRM_ReconfigureWindow( _THIS, SDL_Window * window) {
windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2; 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)) { if (KMSDRM_CreateSurfaces(_this, window)) {
return -1; return -1;
} }
#endif
return 0; return 0;
} }

View File

@ -126,7 +126,9 @@ typedef struct SDL_WindowData
int32_t output_h; int32_t output_h;
int32_t output_x; 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; } SDL_WindowData;
@ -152,7 +154,7 @@ typedef struct KMSDRM_PlaneInfo
} KMSDRM_PlaneInfo; } KMSDRM_PlaneInfo;
/* Helper functions */ /* 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); KMSDRM_FBInfo *KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo);
/* Atomic functions that are used from SDL_kmsdrmopengles.c and SDL_kmsdrmmouse.c */ /* Atomic functions that are used from SDL_kmsdrmopengles.c and SDL_kmsdrmmouse.c */