From 67e0b1dd4e57ff77ce599b3d50804a01d6f18276 Mon Sep 17 00:00:00 2001 From: Manuel Alfayate Corchete Date: Wed, 6 Jan 2021 22:15:26 +0100 Subject: [PATCH] [KMS/DRM] Rewrite KMSDRM_LEGACY backend to accomodate Vulkan compatibility. Fix several bugs on that backend. --- .../kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.c | 559 +++++---- .../kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.h | 13 +- .../SDL_kmsdrm_legacy_opengles.c | 115 +- .../SDL_kmsdrm_legacy_opengles.h | 2 +- .../kmsdrm_legacy/SDL_kmsdrm_legacy_sym.h | 19 + .../kmsdrm_legacy/SDL_kmsdrm_legacy_video.c | 1021 ++++++++++++----- .../kmsdrm_legacy/SDL_kmsdrm_legacy_video.h | 47 +- 7 files changed, 1172 insertions(+), 604 deletions(-) diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.c b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.c index 85a699933..ac1f4202f 100644 --- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.c +++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.c @@ -44,7 +44,32 @@ KMSDRM_LEGACY_CreateDefaultCursor(void) return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY); } -/* Evaluate if a given cursor size is supported or not. Notably, current Intel gfx only support 64x64 and up. */ +/* Converts a pixel from straight-alpha [AA, RR, GG, BB], which the SDL cursor surface has, + to premultiplied-alpha [AA. AA*RR, AA*GG, AA*BB]. + These multiplications have to be done with floats instead of uint32_t's, + and the resulting values have to be converted to be relative to the 0-255 interval, + where 255 is 1.00 and anything between 0 and 255 is 0.xx. */ +void legacy_alpha_premultiply_ARGB8888 (uint32_t *pixel) { + + uint32_t A, R, G, B; + + /* Component bytes extraction. */ + A = (*pixel >> (3 << 3)) & 0xFF; + R = (*pixel >> (2 << 3)) & 0xFF; + G = (*pixel >> (1 << 3)) & 0xFF; + B = (*pixel >> (0 << 3)) & 0xFF; + + /* Alpha pre-multiplication of each component. */ + R = (float)A * ((float)R /255); + G = (float)A * ((float)G /255); + B = (float)A * ((float)B /255); + + /* ARGB8888 pixel recomposition. */ + (*pixel) = (((uint32_t)A << 24) | ((uint32_t)R << 16) | ((uint32_t)G << 8)) | ((uint32_t)B << 0); +} + +/* Evaluate if a given cursor size is supported or not. + Notably, current Intel gfx only support 64x64 and up. */ static SDL_bool KMSDRM_LEGACY_IsCursorSizeSupported (int w, int h, uint32_t bo_format) { @@ -54,7 +79,7 @@ KMSDRM_LEGACY_IsCursorSizeSupported (int w, int h, uint32_t bo_format) { int ret; uint32_t bo_handle; - struct gbm_bo *bo = KMSDRM_LEGACY_gbm_bo_create(viddata->gbm, w, h, bo_format, + struct gbm_bo *bo = KMSDRM_LEGACY_gbm_bo_create(viddata->gbm_dev, w, h, bo_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE); if (!bo) { @@ -63,7 +88,7 @@ KMSDRM_LEGACY_IsCursorSizeSupported (int w, int h, uint32_t bo_format) { } bo_handle = KMSDRM_LEGACY_gbm_bo_get_handle(bo).u32; - ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, bo_handle, w, h); + ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id, bo_handle, w, h); if (ret) { goto cleanup; @@ -80,328 +105,236 @@ cleanup: return SDL_FALSE; } -/* Create a cursor from a surface */ +/* This simply gets the cursor soft-buffer ready. + We don't copy it to a GBO BO until ShowCursor() because the cusor GBM BO (living + in dispata) is destroyed and recreated when we recreate windows, etc. */ static SDL_Cursor * KMSDRM_LEGACY_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y) { - SDL_VideoDevice *dev = SDL_GetVideoDevice(); - SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata); - SDL_PixelFormat *pixlfmt = surface->format; KMSDRM_LEGACY_CursorData *curdata; - SDL_Cursor *cursor; - SDL_bool cursor_supported = SDL_FALSE; - int i, ret, usable_cursor_w, usable_cursor_h; - uint32_t bo_format, bo_stride; - char *buffer = NULL; - size_t bufsize; + SDL_Cursor *cursor, *ret; - switch(pixlfmt->format) { - case SDL_PIXELFORMAT_RGB332: - bo_format = GBM_FORMAT_RGB332; - break; - case SDL_PIXELFORMAT_ARGB4444: - bo_format = GBM_FORMAT_ARGB4444; - break; - case SDL_PIXELFORMAT_RGBA4444: - bo_format = GBM_FORMAT_RGBA4444; - break; - case SDL_PIXELFORMAT_ABGR4444: - bo_format = GBM_FORMAT_ABGR4444; - break; - case SDL_PIXELFORMAT_BGRA4444: - bo_format = GBM_FORMAT_BGRA4444; - break; - case SDL_PIXELFORMAT_ARGB1555: - bo_format = GBM_FORMAT_ARGB1555; - break; - case SDL_PIXELFORMAT_RGBA5551: - bo_format = GBM_FORMAT_RGBA5551; - break; - case SDL_PIXELFORMAT_ABGR1555: - bo_format = GBM_FORMAT_ABGR1555; - break; - case SDL_PIXELFORMAT_BGRA5551: - bo_format = GBM_FORMAT_BGRA5551; - break; - case SDL_PIXELFORMAT_RGB565: - bo_format = GBM_FORMAT_RGB565; - break; - case SDL_PIXELFORMAT_BGR565: - bo_format = GBM_FORMAT_BGR565; - break; - case SDL_PIXELFORMAT_RGB888: - case SDL_PIXELFORMAT_RGB24: - bo_format = GBM_FORMAT_RGB888; - break; - case SDL_PIXELFORMAT_BGR888: - case SDL_PIXELFORMAT_BGR24: - bo_format = GBM_FORMAT_BGR888; - break; - case SDL_PIXELFORMAT_RGBX8888: - bo_format = GBM_FORMAT_RGBX8888; - break; - case SDL_PIXELFORMAT_BGRX8888: - bo_format = GBM_FORMAT_BGRX8888; - break; - case SDL_PIXELFORMAT_ARGB8888: - bo_format = GBM_FORMAT_ARGB8888; - break; - case SDL_PIXELFORMAT_RGBA8888: - bo_format = GBM_FORMAT_RGBA8888; - break; - case SDL_PIXELFORMAT_ABGR8888: - bo_format = GBM_FORMAT_ABGR8888; - break; - case SDL_PIXELFORMAT_BGRA8888: - bo_format = GBM_FORMAT_BGRA8888; - break; - case SDL_PIXELFORMAT_ARGB2101010: - bo_format = GBM_FORMAT_ARGB2101010; - break; - default: - SDL_SetError("Unsupported pixel format for cursor"); - return NULL; - } + curdata = NULL; + ret = NULL; - if (!KMSDRM_LEGACY_gbm_device_is_format_supported(viddata->gbm, bo_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) { - SDL_SetError("Unsupported pixel format for cursor"); - return NULL; - } + /* All code below assumes ARGB8888 format for the cursor surface, + like other backends do. Also, the GBM BO pixels have to be + alpha-premultiplied, but the SDL surface we receive has + straight-alpha pixels, so we always have to convert. */ + SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888); + SDL_assert(surface->pitch == surface->w * 4); cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor)); if (!cursor) { SDL_OutOfMemory(); - return NULL; + goto cleanup; } curdata = (KMSDRM_LEGACY_CursorData *) SDL_calloc(1, sizeof(*curdata)); if (!curdata) { SDL_OutOfMemory(); - SDL_free(cursor); - return NULL; - } - - /* We have to know beforehand if a cursor with the same size as the surface is supported. - * If it's not, we have to find an usable cursor size and use an intermediate and clean buffer. - * If we can't find a cursor size supported by the hardware, we won't go on trying to - * call SDL_SetCursor() later. */ - - usable_cursor_w = surface->w; - usable_cursor_h = surface->h; - - while (usable_cursor_w <= MAX_CURSOR_W && usable_cursor_h <= MAX_CURSOR_H) { - if (KMSDRM_LEGACY_IsCursorSizeSupported(usable_cursor_w, usable_cursor_h, bo_format)) { - cursor_supported = SDL_TRUE; - break; - } - usable_cursor_w += usable_cursor_w; - usable_cursor_h += usable_cursor_h; - } - - if (!cursor_supported) { - SDL_SetError("Could not find a cursor size supported by the kernel driver"); goto cleanup; } + /* hox_x and hot_y are the coordinates of the "tip of the cursor" from it's base. */ curdata->hot_x = hot_x; curdata->hot_y = hot_y; - curdata->w = usable_cursor_w; - curdata->h = usable_cursor_h; + curdata->w = surface->w; + curdata->h = surface->h; + curdata->buffer = NULL; - curdata->bo = KMSDRM_LEGACY_gbm_bo_create(viddata->gbm, usable_cursor_w, usable_cursor_h, bo_format, - GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE); + /* Configure the cursor buffer info. + This buffer has the original size of the cursor surface we are given. */ + curdata->buffer_pitch = surface->pitch; + curdata->buffer_size = surface->pitch * surface->h; + curdata->buffer = (uint32_t*)SDL_malloc(curdata->buffer_size); - if (!curdata->bo) { - SDL_SetError("Could not create GBM cursor BO"); + if (!curdata->buffer) { + SDL_OutOfMemory(); goto cleanup; } - bo_stride = KMSDRM_LEGACY_gbm_bo_get_stride(curdata->bo); - bufsize = bo_stride * curdata->h; - - if (surface->pitch != bo_stride) { - /* pitch doesn't match stride, must be copied to temp buffer */ - buffer = SDL_malloc(bufsize); - if (!buffer) { - SDL_OutOfMemory(); + if (SDL_MUSTLOCK(surface)) { + if (SDL_LockSurface(surface) < 0) { + /* Could not lock surface */ goto cleanup; } + } - if (SDL_MUSTLOCK(surface)) { - if (SDL_LockSurface(surface) < 0) { - /* Could not lock surface */ - goto cleanup; - } - } + /* Copy the surface pixels to the cursor buffer, for future use in ShowCursor() */ + SDL_memcpy(curdata->buffer, surface->pixels, curdata->buffer_size); - /* Clean the whole temporary buffer */ - SDL_memset(buffer, 0x00, bo_stride * curdata->h); - - /* Copy to temporary buffer */ - for (i = 0; i < surface->h; i++) { - SDL_memcpy(buffer + (i * bo_stride), - ((char *)surface->pixels) + (i * surface->pitch), - surface->w * pixlfmt->BytesPerPixel); - } - - if (SDL_MUSTLOCK(surface)) { - SDL_UnlockSurface(surface); - } - - if (KMSDRM_LEGACY_gbm_bo_write(curdata->bo, buffer, bufsize)) { - SDL_SetError("Could not write to GBM cursor BO"); - goto cleanup; - } - - /* Free temporary buffer */ - SDL_free(buffer); - buffer = NULL; - } else { - /* surface matches BO format */ - if (SDL_MUSTLOCK(surface)) { - if (SDL_LockSurface(surface) < 0) { - /* Could not lock surface */ - goto cleanup; - } - } - - ret = KMSDRM_LEGACY_gbm_bo_write(curdata->bo, surface->pixels, bufsize); - - if (SDL_MUSTLOCK(surface)) { - SDL_UnlockSurface(surface); - } - - if (ret) { - SDL_SetError("Could not write to GBM cursor BO"); - goto cleanup; - } + if (SDL_MUSTLOCK(surface)) { + SDL_UnlockSurface(surface); } cursor->driverdata = curdata; - return cursor; + ret = cursor; cleanup: - if (buffer) { - SDL_free(buffer); + if (ret == NULL) { + if (curdata) { + if (curdata->buffer) { + SDL_free(curdata->buffer); + } + SDL_free(curdata); + } + if (cursor) { + SDL_free(cursor); + } } - if (cursor) { - SDL_free(cursor); - } - if (curdata) { - if (curdata->bo) { - KMSDRM_LEGACY_gbm_bo_destroy(curdata->bo); - } - SDL_free(curdata); - } - return NULL; + + return ret; } -/* Show the specified cursor, or hide if cursor is NULL */ +/* When we create a window, we have to test if we have to show the cursor, + and explicily do so if necessary. + This is because when we destroy a window, we take the cursor away from the + cursor plane, and destroy the cusror GBM BO. So we have to re-show it, + so to say. */ +void +KMSDRM_LEGACY_InitCursor() +{ + SDL_Mouse *mouse = NULL; + mouse = SDL_GetMouse(); + + if (!mouse) { + return; + } + if (!(mouse->cur_cursor)) { + return; + } + + if (!(mouse->cursor_shown)) { + return; + } + + KMSDRM_LEGACY_ShowCursor(mouse->cur_cursor); +} + +/* Show the specified cursor, or hide if cursor is NULL. */ static int KMSDRM_LEGACY_ShowCursor(SDL_Cursor * cursor) { - SDL_VideoDevice *dev = SDL_GetVideoDevice(); - SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata); + SDL_VideoDevice *video_device = SDL_GetVideoDevice(); + SDL_VideoData *viddata = ((SDL_VideoData *)video_device->driverdata); + SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); SDL_Mouse *mouse; KMSDRM_LEGACY_CursorData *curdata; - SDL_VideoDisplay *display = NULL; - SDL_DisplayData *dispdata = NULL; - int ret; + uint32_t bo_handle; + size_t bo_stride; + size_t bufsize; + uint32_t *ready_buffer = NULL; + uint32_t pixel; + + int i,j; + int ret; + mouse = SDL_GetMouse(); if (!mouse) { return SDL_SetError("No mouse."); } - if (mouse->focus) { - display = SDL_GetDisplayForWindow(mouse->focus); - if (display) { - dispdata = (SDL_DisplayData*) display->driverdata; - } - } - - if (!cursor) { - /* Hide current cursor */ - if (mouse->cur_cursor && mouse->cur_cursor->driverdata) { - curdata = (KMSDRM_LEGACY_CursorData *) mouse->cur_cursor->driverdata; - - if (curdata->crtc_id != 0) { - ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, curdata->crtc_id, 0, 0, 0); - if (ret) { - SDL_SetError("Could not hide current cursor with drmModeSetCursor()."); - return ret; - } - /* Mark previous cursor as not-displayed */ - curdata->crtc_id = 0; - - return 0; - } - } - /* otherwise if possible, hide global cursor */ - if (dispdata && dispdata->crtc_id != 0) { - ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, 0, 0, 0); - if (ret) { - SDL_SetError("Could not hide display's cursor with drmModeSetCursor()."); - return ret; - } - return 0; - } - - return SDL_SetError("Couldn't find cursor to hide."); - } - /* If cursor != NULL, show new cursor on display */ - if (!display) { - return SDL_SetError("Could not get display for mouse."); - } - if (!dispdata) { - return SDL_SetError("Could not get display driverdata."); - } - - curdata = (KMSDRM_LEGACY_CursorData *) cursor->driverdata; - if (!curdata || !curdata->bo) { - return SDL_SetError("Cursor not initialized properly."); - } - - bo_handle = KMSDRM_LEGACY_gbm_bo_get_handle(curdata->bo).u32; - if (curdata->hot_x == 0 && curdata->hot_y == 0) { - ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, bo_handle, - curdata->w, curdata->h); - } else { - ret = KMSDRM_LEGACY_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc_id, bo_handle, - curdata->w, curdata->h, curdata->hot_x, curdata->hot_y); - } - if (ret) { - SDL_SetError("drmModeSetCursor failed."); + /*********************************************************/ + /* Hide cursor if it's NULL or it has no focus(=winwow). */ + /*********************************************************/ + if (!cursor || !mouse->focus) { + /* Hide the drm cursor with no more considerations because + SDL_VideoQuit() takes us here after disabling the mouse + so there is no mouse->cur_cursor by now. */ + ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, + dispdata->crtc->crtc_id, 0, 0, 0); + if (ret) { + ret = SDL_SetError("Could not hide current cursor with drmModeSetCursor()."); + } return ret; } - curdata->crtc_id = dispdata->crtc_id; + /************************************************/ + /* If cursor != NULL, DO show cursor on display */ + /************************************************/ + if (!dispdata) { + return SDL_SetError("Could not get display driverdata."); + } + + curdata = (KMSDRM_LEGACY_CursorData *) cursor->driverdata; - return 0; + if (!curdata || !dispdata->cursor_bo) { + return SDL_SetError("Cursor not initialized properly."); + } + + /* Prepare a buffer we can dump to our GBM BO (different + size, alpha premultiplication...) */ + bo_stride = KMSDRM_LEGACY_gbm_bo_get_stride(dispdata->cursor_bo); + bufsize = bo_stride * curdata->h; + + ready_buffer = (uint32_t*)SDL_malloc(bufsize); + if (!ready_buffer) { + ret = SDL_OutOfMemory(); + goto cleanup; + } + + /* Clean the whole buffer we are preparing. */ + SDL_memset(ready_buffer, 0x00, bo_stride * curdata->h); + + /* Copy from the cursor buffer to a buffer that we can dump to the GBM BO, + pre-multiplying by alpha each pixel as we go. */ + for (i = 0; i < curdata->h; i++) { + for (j = 0; j < curdata->w; j++) { + pixel = ((uint32_t*)curdata->buffer)[i * curdata->w + j]; + legacy_alpha_premultiply_ARGB8888 (&pixel); + SDL_memcpy(ready_buffer + (i * dispdata->cursor_w) + j, &pixel, 4); + } + } + + /* Dump the cursor buffer to our GBM BO. */ + if (KMSDRM_LEGACY_gbm_bo_write(dispdata->cursor_bo, ready_buffer, bufsize)) { + ret = SDL_SetError("Could not write to GBM cursor BO"); + goto cleanup; + } + + /* Put the GBM BO buffer on screen using the DRM interface. */ + bo_handle = KMSDRM_LEGACY_gbm_bo_get_handle(dispdata->cursor_bo).u32; + if (curdata->hot_x == 0 && curdata->hot_y == 0) { + ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id, + bo_handle, curdata->w, curdata->h); + } else { + ret = KMSDRM_LEGACY_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc->crtc_id, + bo_handle, curdata->w, curdata->h, curdata->hot_x, curdata->hot_y); + } + + if (ret) { + ret = SDL_SetError("Failed to set DRM cursor."); + goto cleanup; + } + +cleanup: + + if (ready_buffer) { + SDL_free(ready_buffer); + } + return ret; } -/* Free a window manager cursor */ +/* We have destroyed the cursor by now, in KMSDRM_DestroyCursor. + This is only for freeing the SDL_cursor.*/ static void KMSDRM_LEGACY_FreeCursor(SDL_Cursor * cursor) { KMSDRM_LEGACY_CursorData *curdata; - int drm_fd; + /* Even if the cursor is not ours, free it. */ if (cursor) { curdata = (KMSDRM_LEGACY_CursorData *) cursor->driverdata; - - if (curdata) { - if (curdata->bo) { - if (curdata->crtc_id != 0) { - drm_fd = KMSDRM_LEGACY_gbm_device_get_fd(KMSDRM_LEGACY_gbm_bo_get_device(curdata->bo)); - /* Hide the cursor if previously shown on a CRTC */ - KMSDRM_LEGACY_drmModeSetCursor(drm_fd, curdata->crtc_id, 0, 0, 0); - curdata->crtc_id = 0; - } - KMSDRM_LEGACY_gbm_bo_destroy(curdata->bo); - curdata->bo = NULL; - } + /* Free cursor buffer */ + if (curdata->buffer) { + SDL_free(curdata->buffer); + curdata->buffer = NULL; + } + /* Free cursor itself */ + if (cursor->driverdata) { SDL_free(cursor->driverdata); } SDL_free(cursor); @@ -420,44 +353,57 @@ KMSDRM_LEGACY_WarpMouse(SDL_Window * window, int x, int y) static int KMSDRM_LEGACY_WarpMouseGlobal(int x, int y) { - KMSDRM_LEGACY_CursorData *curdata; SDL_Mouse *mouse = SDL_GetMouse(); + SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) { /* Update internal mouse position. */ SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y); /* And now update the cursor graphic position on screen. */ - curdata = (KMSDRM_LEGACY_CursorData *) mouse->cur_cursor->driverdata; - if (curdata->bo) { + if (dispdata->cursor_bo) { + int ret, drm_fd; + drm_fd = KMSDRM_LEGACY_gbm_device_get_fd( + KMSDRM_LEGACY_gbm_bo_get_device(dispdata->cursor_bo)); + ret = KMSDRM_LEGACY_drmModeMoveCursor(drm_fd, dispdata->crtc->crtc_id, x, y); - if (curdata->crtc_id != 0) { - int ret, drm_fd; - drm_fd = KMSDRM_LEGACY_gbm_device_get_fd(KMSDRM_LEGACY_gbm_bo_get_device(curdata->bo)); - ret = KMSDRM_LEGACY_drmModeMoveCursor(drm_fd, curdata->crtc_id, x, y); + if (ret) { + SDL_SetError("drmModeMoveCursor() failed."); + } - if (ret) { - SDL_SetError("drmModeMoveCursor() failed."); - } + return ret; - return ret; - } else { - return SDL_SetError("Cursor is not currently shown."); - } } else { return SDL_SetError("Cursor not initialized properly."); } } else { return SDL_SetError("No mouse or current cursor."); } + + return 0; } +/* UNDO WHAT WE DID IN KMSDRM_InitMouse(). */ +void +KMSDRM_LEGACY_DeinitMouse(_THIS) +{ + SDL_VideoDevice *video_device = SDL_GetVideoDevice(); + SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); + + /* Destroy the curso GBM BO. */ + if (video_device && dispdata->cursor_bo) { + KMSDRM_LEGACY_gbm_bo_destroy(dispdata->cursor_bo); + dispdata->cursor_bo = NULL; + } +} + +/* Create cursor BO. */ void KMSDRM_LEGACY_InitMouse(_THIS) { - /* FIXME: Using UDEV it should be possible to scan all mice - * but there's no point in doing so as there's no multimice support...yet! - */ + SDL_VideoDevice *dev = SDL_GetVideoDevice(); + SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata); + SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); SDL_Mouse *mouse = SDL_GetMouse(); mouse->CreateCursor = KMSDRM_LEGACY_CreateCursor; @@ -467,7 +413,53 @@ KMSDRM_LEGACY_InitMouse(_THIS) mouse->WarpMouse = KMSDRM_LEGACY_WarpMouse; mouse->WarpMouseGlobal = KMSDRM_LEGACY_WarpMouseGlobal; + /************************************************/ + /* Create the cursor GBM BO, if we haven't yet. */ + /************************************************/ + if (!dispdata->cursor_bo) { + + if (!KMSDRM_LEGACY_gbm_device_is_format_supported(viddata->gbm_dev, + GBM_FORMAT_ARGB8888, + GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) + { + SDL_SetError("Unsupported pixel format for cursor"); + return; + } + + if (KMSDRM_LEGACY_drmGetCap(viddata->drm_fd, + DRM_CAP_CURSOR_WIDTH, &dispdata->cursor_w) || + KMSDRM_LEGACY_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_HEIGHT, + &dispdata->cursor_h)) + { + SDL_SetError("Could not get the recommended GBM cursor size"); + goto cleanup; + } + + if (dispdata->cursor_w == 0 || dispdata->cursor_h == 0) { + SDL_SetError("Could not get an usable GBM cursor size"); + goto cleanup; + } + + dispdata->cursor_bo = KMSDRM_LEGACY_gbm_bo_create(viddata->gbm_dev, + dispdata->cursor_w, dispdata->cursor_h, + GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE); + + if (!dispdata->cursor_bo) { + SDL_SetError("Could not create GBM cursor BO"); + goto cleanup; + } + } + + /* SDL expects to set the default cursor on screen when we init the mouse. */ SDL_SetDefaultCursor(KMSDRM_LEGACY_CreateDefaultCursor()); + + return; + +cleanup: + if (dispdata->cursor_bo) { + KMSDRM_LEGACY_gbm_bo_destroy(dispdata->cursor_bo); + dispdata->cursor_bo = NULL; + } } void @@ -481,15 +473,14 @@ static void KMSDRM_LEGACY_MoveCursor(SDL_Cursor * cursor) { SDL_Mouse *mouse = SDL_GetMouse(); - KMSDRM_LEGACY_CursorData *curdata; + SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); int drm_fd, ret; /* We must NOT call SDL_SendMouseMotion() here or we will enter recursivity! That's why we move the cursor graphic ONLY. */ if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) { - curdata = (KMSDRM_LEGACY_CursorData *) mouse->cur_cursor->driverdata; - drm_fd = KMSDRM_LEGACY_gbm_device_get_fd(KMSDRM_LEGACY_gbm_bo_get_device(curdata->bo)); - ret = KMSDRM_LEGACY_drmModeMoveCursor(drm_fd, curdata->crtc_id, mouse->x, mouse->y); + drm_fd = KMSDRM_LEGACY_gbm_device_get_fd(KMSDRM_LEGACY_gbm_bo_get_device(dispdata->cursor_bo)); + ret = KMSDRM_LEGACY_drmModeMoveCursor(drm_fd, dispdata->crtc->crtc_id, mouse->x, mouse->y); if (ret) { SDL_SetError("drmModeMoveCursor() failed."); diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.h b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.h index 46e112295..e2bc96871 100644 --- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.h +++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.h @@ -31,15 +31,24 @@ typedef struct _KMSDRM_LEGACY_CursorData { - struct gbm_bo *bo; - uint32_t crtc_id; int hot_x, hot_y; int w, h; + + /* The buffer where we store the mouse bitmap ready to be used. + We get it ready and filled in CreateCursor(), and copy it + to a GBM BO in ShowCursor().*/ + uint32_t *buffer; + size_t buffer_size; + size_t buffer_pitch; + } KMSDRM_LEGACY_CursorData; extern void KMSDRM_LEGACY_InitMouse(_THIS); +extern void KMSDRM_LEGACY_DeinitMouse(_THIS); extern void KMSDRM_LEGACY_QuitMouse(_THIS); +extern void KMSDRM_LEGACY_InitCursor(); + #endif /* SDL_KMSDRM_LEGACY_mouse_h_ */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.c b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.c index f522a4b9f..bc3d58ae0 100644 --- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.c +++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.c @@ -35,10 +35,36 @@ /* EGL implementation of SDL OpenGL support */ +void +KMSDRM_LEGACY_GLES_DefaultProfileConfig(_THIS, int *mask, int *major, int *minor) +{ + /* if SDL was _also_ built with the Raspberry Pi driver (so we're + definitely a Pi device), default to GLES2. */ +#if SDL_VIDEO_DRIVER_RPI + *mask = SDL_GL_CONTEXT_PROFILE_ES; + *major = 2; + *minor = 0; +#endif +} + int KMSDRM_LEGACY_GLES_LoadLibrary(_THIS, const char *path) { - NativeDisplayType display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm; + /* Just pretend you do this here, but don't do it until KMSDRM_CreateWindow(), + where we do the same library load we would normally do here. + because this gets called by SDL_CreateWindow() before KMSDR_CreateWindow(), + so gbm dev isn't yet created when this is called, AND we can't alter the + call order in SDL_CreateWindow(). */ +#if 0 + NativeDisplayType display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm_dev; return SDL_EGL_LoadLibrary(_this, path, display, EGL_PLATFORM_GBM_MESA); +#endif + return 0; +} + +void +KMSDRM_LEGACY_GLES_UnloadLibrary(_THIS) { + /* As with KMSDRM_GLES_LoadLibrary(), we define our own "dummy" unloading function + so we manually unload the library whenever we want. */ } SDL_EGL_CreateContext_impl(KMSDRM_LEGACY) @@ -81,16 +107,17 @@ KMSDRM_LEGACY_GLES_SwapWindow(_THIS, SDL_Window * window) { } /* Release the previous front buffer */ - if (windata->curr_bo) { - KMSDRM_LEGACY_gbm_surface_release_buffer(windata->gs, windata->curr_bo); - /* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Released GBM surface %p", (void *)windata->curr_bo); */ - windata->curr_bo = NULL; + if (windata->bo) { + KMSDRM_LEGACY_gbm_surface_release_buffer(windata->gs, windata->bo); + windata->bo = NULL; } - windata->curr_bo = windata->next_bo; + windata->bo = windata->next_bo; - /* Make the current back buffer the next front buffer */ - if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface))) { + /* Mark a buffer to becume the next front buffer. + This won't happen until pagelip completes. */ + if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, + windata->egl_surface))) { SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "eglSwapBuffers failed."); return 0; } @@ -100,46 +127,60 @@ KMSDRM_LEGACY_GLES_SwapWindow(_THIS, SDL_Window * window) { if (!windata->next_bo) { SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not lock GBM surface front buffer"); return 0; - /* } else { - SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Locked GBM surface %p", (void *)windata->next_bo); */ } + /* Get the fb_info for the next front buffer. */ fb_info = KMSDRM_LEGACY_FBFromBO(_this, windata->next_bo); if (!fb_info) { return 0; } - if (!windata->curr_bo) { - /* On the first swap, immediately present the new front buffer. Before - drmModePageFlip can be used the CRTC has to be configured to use - the current connector and mode with drmModeSetCrtc */ - ret = KMSDRM_LEGACY_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc_id, fb_info->fb_id, 0, - 0, &dispdata->conn->connector_id, 1, &dispdata->mode); + if (!windata->bo) { + /***************************************************************************/ + /* This is fundamental. */ + /* We can't display an fb smaller than the resolution currently configured */ + /* on the CRTC, because the CRTC would be scanning out of bounds. */ + /* So instead of using drmModeSetCrtc() to tell CRTC to scan the fb */ + /* directly, we use a plane (overlay or primary, doesn't mind if we */ + /* activated the UNVERSAL PLANES cap) to scale the buffer to the */ + /* resolution currently configured on the CRTC. */ + /* */ + /* We can't do this sooner, on CreateWindow(), because we don't have a */ + /* framebuffer there yet, and DRM doesn't like 0 or -1 as the fb_id. */ + /***************************************************************************/ + ret = KMSDRM_LEGACY_drmModeSetPlane(viddata->drm_fd, dispdata->plane_id, + dispdata->crtc->crtc_id, fb_info->fb_id, 0, + windata->output_x, 0, + windata->output_w, windata->output_h, + 0, 0, + windata->src_w << 16, windata->src_h << 16); if (ret) { - SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not configure CRTC"); - } - } else { - /* On subsequent swaps, queue the new front buffer to be flipped during - the next vertical blank */ - ret = KMSDRM_LEGACY_drmModePageFlip(viddata->drm_fd, dispdata->crtc_id, fb_info->fb_id, - DRM_MODE_PAGE_FLIP_EVENT, &windata->waiting_for_flip); - /* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "drmModePageFlip(%d, %u, %u, DRM_MODE_PAGE_FLIP_EVENT, &windata->waiting_for_flip)", - viddata->drm_fd, displaydata->crtc_id, fb_info->fb_id); */ - - if (_this->egl_data->egl_swapinterval == 1) { - if (ret == 0) { - windata->waiting_for_flip = SDL_TRUE; - } else { - SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d", ret); - } + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not set PLANE"); } - /* Wait immediately for vsync (as if we only had two buffers), for low input-lag scenarios. - Run your SDL2 program with "SDL_KMSDRM_LEGACY_DOUBLE_BUFFER=1 " to enable this. */ - if (_this->egl_data->egl_swapinterval == 1 && windata->double_buffer) { - KMSDRM_LEGACY_WaitPageFlip(_this, windata, -1); - } + return 0; + } + + /* Issue pageflip on the next front buffer. + The pageflip will be done during the next vblank. */ + ret = KMSDRM_LEGACY_drmModePageFlip(viddata->drm_fd, dispdata->crtc->crtc_id, + fb_info->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &windata->waiting_for_flip); + + if (_this->egl_data->egl_swapinterval == 1) { + if (ret == 0) { + windata->waiting_for_flip = SDL_TRUE; + } else { + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d", ret); + } + } + + /* If we are in double-buffer mode, wait immediately for vsync + (as if we only had two buffers), + Run your SDL2 program with "SDL_KMSDRM_LEGACY_DOUBLE_BUFFER=1 " + to enable this. */ + if (_this->egl_data->egl_swapinterval == 1 && windata->double_buffer) { + KMSDRM_LEGACY_WaitPageFlip(_this, windata, -1); } return 0; diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.h b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.h index 0769f490e..e1314b7e1 100644 --- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.h +++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.h @@ -31,10 +31,10 @@ /* OpenGLES functions */ #define KMSDRM_LEGACY_GLES_GetAttribute SDL_EGL_GetAttribute #define KMSDRM_LEGACY_GLES_GetProcAddress SDL_EGL_GetProcAddress -#define KMSDRM_LEGACY_GLES_UnloadLibrary SDL_EGL_UnloadLibrary #define KMSDRM_LEGACY_GLES_DeleteContext SDL_EGL_DeleteContext #define KMSDRM_LEGACY_GLES_GetSwapInterval SDL_EGL_GetSwapInterval +extern void KMSDRM_LEGACY_GLES_DefaultProfileConfig(_THIS, int *mask, int *major, int *minor); extern int KMSDRM_LEGACY_GLES_SetSwapInterval(_THIS, int interval); extern int KMSDRM_LEGACY_GLES_LoadLibrary(_THIS, const char *path); extern SDL_GLContext KMSDRM_LEGACY_GLES_CreateContext(_THIS, SDL_Window * window); diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_sym.h b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_sym.h index 1b4ecab1b..487dc1969 100644 --- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_sym.h +++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_sym.h @@ -40,6 +40,7 @@ SDL_KMSDRM_LEGACY_SYM(void,drmModeFreeFB,(drmModeFBPtr ptr)) SDL_KMSDRM_LEGACY_SYM(void,drmModeFreeCrtc,(drmModeCrtcPtr ptr)) SDL_KMSDRM_LEGACY_SYM(void,drmModeFreeConnector,(drmModeConnectorPtr ptr)) SDL_KMSDRM_LEGACY_SYM(void,drmModeFreeEncoder,(drmModeEncoderPtr ptr)) +SDL_KMSDRM_LEGACY_SYM(int,drmGetCap,(int fd, uint64_t capability, uint64_t *value)) SDL_KMSDRM_LEGACY_SYM(drmModeResPtr,drmModeGetResources,(int fd)) SDL_KMSDRM_LEGACY_SYM(int,drmModeAddFB,(int fd, uint32_t width, uint32_t height, uint8_t depth, uint8_t bpp, uint32_t pitch, uint32_t bo_handle, @@ -62,6 +63,24 @@ SDL_KMSDRM_LEGACY_SYM(int,drmHandleEvent,(int fd,drmEventContextPtr evctx)) SDL_KMSDRM_LEGACY_SYM(int,drmModePageFlip,(int fd, uint32_t crtc_id, uint32_t fb_id, uint32_t flags, void *user_data)) +/* Planes stuff. */ +SDL_KMSDRM_LEGACY_SYM(int,drmSetClientCap,(int fd, uint64_t capability, uint64_t value)) +SDL_KMSDRM_LEGACY_SYM(drmModePlaneResPtr,drmModeGetPlaneResources,(int fd)) +SDL_KMSDRM_LEGACY_SYM(drmModePlanePtr,drmModeGetPlane,(int fd, uint32_t plane_id)) +SDL_KMSDRM_LEGACY_SYM(drmModeObjectPropertiesPtr,drmModeObjectGetProperties,(int fd,uint32_t object_id,uint32_t object_type)) +SDL_KMSDRM_LEGACY_SYM(drmModePropertyPtr,drmModeGetProperty,(int fd, uint32_t propertyId)) + +SDL_KMSDRM_LEGACY_SYM(void,drmModeFreeProperty,(drmModePropertyPtr ptr)) +SDL_KMSDRM_LEGACY_SYM(void,drmModeFreeObjectProperties,(drmModeObjectPropertiesPtr ptr)) +SDL_KMSDRM_LEGACY_SYM(void,drmModeFreePlane,(drmModePlanePtr ptr)) +SDL_KMSDRM_LEGACY_SYM(void,drmModeFreePlaneResources,(drmModePlaneResPtr ptr)) +SDL_KMSDRM_LEGACY_SYM(int,drmModeSetPlane,(int fd, uint32_t plane_id, uint32_t crtc_id, + uint32_t fb_id, uint32_t flags, + int32_t crtc_x, int32_t crtc_y, + uint32_t crtc_w, uint32_t crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h)) +/* Planes stuff ends. */ SDL_KMSDRM_LEGACY_MODULE(GBM) SDL_KMSDRM_LEGACY_SYM(int,gbm_device_get_fd,(struct gbm_device *gbm)) diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.c b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.c index db3327898..445d8cbd8 100644 --- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.c +++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.c @@ -61,6 +61,106 @@ #define KMSDRM_LEGACY_DRI_CARDPATHFMT "/dev/dri/card%d" #endif +#if 0 + +void print_plane_info(_THIS, drmModePlanePtr plane) +{ + char *plane_type; + drmModeRes *resources; + uint32_t type = 0; + SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); + int i; + + drmModeObjectPropertiesPtr props = KMSDRM_LEGACY_drmModeObjectGetProperties(viddata->drm_fd, + plane->plane_id, DRM_MODE_OBJECT_PLANE); + + /* Search the plane props for the plane type. */ + for (i = 0; i < props->count_props; i++) { + drmModePropertyPtr p = KMSDRM_LEGACY_drmModeGetProperty(viddata->drm_fd, props->props[i]); + if ((strcmp(p->name, "type") == 0)) { + type = props->prop_values[i]; + } + + KMSDRM_LEGACY_drmModeFreeProperty(p); + } + + switch (type) { + case DRM_PLANE_TYPE_OVERLAY: + plane_type = "overlay"; + break; + + case DRM_PLANE_TYPE_PRIMARY: + plane_type = "primary"; + break; + + case DRM_PLANE_TYPE_CURSOR: + plane_type = "cursor"; + break; + } + + + /* Remember that to present a plane on screen, it has to be + connected to a CRTC so the CRTC scans it, + scales it, etc... and presents it on screen. */ + + /* Now we look for the CRTCs supported by the plane. */ + resources = KMSDRM_LEGACY_drmModeGetResources(viddata->drm_fd); + if (!resources) + return; + + printf("--PLANE ID: %d\nPLANE TYPE: %s\nCRTC READING THIS PLANE: %d\nCRTCS SUPPORTED BY THIS PLANE: ", plane->plane_id, plane_type, plane->crtc_id); + for (i = 0; i < resources->count_crtcs; i++) { + if (plane->possible_crtcs & (1 << i)) { + uint32_t crtc_id = resources->crtcs[i]; + printf ("%d", crtc_id); + break; + } + } + + printf ("\n\n"); +} + +void get_planes_info(_THIS) +{ + drmModePlaneResPtr plane_resources; + uint32_t i; + + SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); + SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); + + plane_resources = KMSDRM_LEGACY_drmModeGetPlaneResources(viddata->drm_fd); + if (!plane_resources) { + printf("drmModeGetPlaneResources failed: %s\n", strerror(errno)); + return; + } + + printf("--Number of planes found: %d-- \n", plane_resources->count_planes); + printf("--Usable CRTC that we have chosen: %d-- \n", dispdata->crtc->crtc_id); + + /* Iterate on all the available planes. */ + for (i = 0; (i < plane_resources->count_planes); i++) { + + uint32_t plane_id = plane_resources->planes[i]; + + drmModePlanePtr plane = KMSDRM_LEGACY_drmModeGetPlane(viddata->drm_fd, plane_id); + if (!plane) { + printf("drmModeGetPlane(%u) failed: %s\n", plane_id, strerror(errno)); + continue; + } + + /* Print plane info. */ + print_plane_info(_this, plane); + KMSDRM_LEGACY_drmModeFreePlane(plane); + } + + KMSDRM_LEGACY_drmModeFreePlaneResources(plane_resources); +} + +#endif + + + + static int check_modestting(int devindex) { @@ -216,6 +316,7 @@ KMSDRM_LEGACY_CreateDevice(int devindex) device->SetWindowIcon = KMSDRM_LEGACY_SetWindowIcon; device->SetWindowPosition = KMSDRM_LEGACY_SetWindowPosition; device->SetWindowSize = KMSDRM_LEGACY_SetWindowSize; + device->SetWindowFullscreen = KMSDRM_LEGACY_SetWindowFullscreen; device->ShowWindow = KMSDRM_LEGACY_ShowWindow; device->HideWindow = KMSDRM_LEGACY_HideWindow; device->RaiseWindow = KMSDRM_LEGACY_RaiseWindow; @@ -364,113 +465,64 @@ KMSDRM_LEGACY_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout) { /* SDL Video and Display initialization/handling functions */ /* _this is a SDL_VideoDevice * */ /*****************************************************************************/ -static void -KMSDRM_LEGACY_DestroySurfaces(_THIS, SDL_Window * window) -{ - SDL_WindowData *windata = (SDL_WindowData *)window->driverdata; - KMSDRM_LEGACY_WaitPageFlip(_this, windata, -1); - - if (windata->curr_bo) { - KMSDRM_LEGACY_gbm_surface_release_buffer(windata->gs, windata->curr_bo); - windata->curr_bo = NULL; +/* Deinitializes the dispdata members needed for KMSDRM operation that are + inoffeensive for VK compatibility. */ +void KMSDRM_LEGACY_DisplayDataDeinit (_THIS, SDL_DisplayData *dispdata) { + /* Free connector */ + if (dispdata && dispdata->connector) { + KMSDRM_LEGACY_drmModeFreeConnector(dispdata->connector); + dispdata->connector = NULL; } - if (windata->next_bo) { - KMSDRM_LEGACY_gbm_surface_release_buffer(windata->gs, windata->next_bo); - windata->next_bo = NULL; - } - - 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; - } - - if (windata->gs) { - KMSDRM_LEGACY_gbm_surface_destroy(windata->gs); - windata->gs = NULL; + /* Free CRTC */ + if (dispdata && dispdata->crtc) { + KMSDRM_LEGACY_drmModeFreeCrtc(dispdata->crtc); + dispdata->crtc = NULL; } } -int -KMSDRM_LEGACY_CreateSurfaces(_THIS, SDL_Window * window) -{ +/* Initializes the dispdata members needed for KMSDRM operation that are + inoffeensive for VK compatibility, except we must leave the drm_fd + closed when we get to the end of this function. + This is to be called early, in VideoInit(), because it gets us + the videomode information, which SDL needs immediately after VideoInit(). */ +int KMSDRM_LEGACY_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) { SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); - SDL_WindowData *windata = (SDL_WindowData *)window->driverdata; - SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; - Uint32 width = dispdata->mode.hdisplay; - Uint32 height = dispdata->mode.vdisplay; - Uint32 surface_fmt = GBM_FORMAT_XRGB8888; - Uint32 surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; - EGLContext egl_context; - if (!KMSDRM_LEGACY_gbm_device_is_format_supported(viddata->gbm, surface_fmt, surface_flags)) { - SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway."); - } - - SDL_EGL_SetRequiredVisualId(_this, surface_fmt); - egl_context = (EGLContext)SDL_GL_GetCurrentContext(); - - KMSDRM_LEGACY_DestroySurfaces(_this, window); - - windata->gs = KMSDRM_LEGACY_gbm_surface_create(viddata->gbm, width, height, surface_fmt, surface_flags); - - if (!windata->gs) { - return SDL_SetError("Could not create GBM surface"); - } - - 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; - - return 0; -} - -int -KMSDRM_LEGACY_VideoInit(_THIS) -{ - int i, j, ret = 0; - SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); - SDL_DisplayData *dispdata = NULL; drmModeRes *resources = NULL; + drmModePlaneRes *plane_resources = NULL; drmModeEncoder *encoder = NULL; - char devname[32]; - SDL_VideoDisplay display = {0}; + drmModeConnector *connector = NULL; + drmModeCrtc *crtc = NULL; - dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData)); + uint32_t crtc_index = 0; - if (!dispdata) { - return SDL_OutOfMemory(); - } + int ret = 0; + unsigned i,j; - SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_LEGACY_VideoInit()"); + dispdata->modeset_pending = SDL_FALSE; + dispdata->gbm_init = SDL_FALSE; + + dispdata->cursor_bo = NULL; + + dispdata->plane_id = 0; /* Open /dev/dri/cardNN (/dev/drmN if on OpenBSD) */ - SDL_snprintf(devname, sizeof(devname), KMSDRM_LEGACY_DRI_CARDPATHFMT, viddata->devindex); + SDL_snprintf(viddata->devpath, sizeof(viddata->devpath), KMSDRM_LEGACY_DRI_CARDPATHFMT, viddata->devindex); - SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", devname); - viddata->drm_fd = open(devname, O_RDWR | O_CLOEXEC); + SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", viddata->devpath); + viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC); if (viddata->drm_fd < 0) { - ret = SDL_SetError("Could not open %s", devname); + ret = SDL_SetError("Could not open %s", viddata->devpath); goto cleanup; } SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd); - viddata->gbm = KMSDRM_LEGACY_gbm_create_device(viddata->drm_fd); - if (!viddata->gbm) { - ret = SDL_SetError("Couldn't create gbm device."); - goto cleanup; - } + /* Activate universal planes. */ + KMSDRM_LEGACY_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); /* Get all of the available connectors / devices / crtcs */ resources = KMSDRM_LEGACY_drmModeGetResources(viddata->drm_fd); @@ -479,24 +531,24 @@ KMSDRM_LEGACY_VideoInit(_THIS) goto cleanup; } + /* Iterate on the available connectors to find a connected connector. */ for (i = 0; i < resources->count_connectors; i++) { - drmModeConnector *conn = KMSDRM_LEGACY_drmModeGetConnector(viddata->drm_fd, resources->connectors[i]); + drmModeConnector *conn = KMSDRM_LEGACY_drmModeGetConnector(viddata->drm_fd, + resources->connectors[i]); if (!conn) { continue; } if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) { - SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found connector %d with %d modes.", - conn->connector_id, conn->count_modes); - dispdata->conn = conn; + connector = conn; break; } KMSDRM_LEGACY_drmModeFreeConnector(conn); } - if (!dispdata->conn) { + if (!connector) { ret = SDL_SetError("No currently active connector found."); goto cleanup; } @@ -509,8 +561,7 @@ KMSDRM_LEGACY_VideoInit(_THIS) continue; } - if (encoder->encoder_id == dispdata->conn->encoder_id) { - SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id); + if (encoder->encoder_id == connector->encoder_id) { break; } @@ -521,19 +572,20 @@ KMSDRM_LEGACY_VideoInit(_THIS) if (!encoder) { /* No encoder was connected, find the first supported one */ for (i = 0; i < resources->count_encoders; i++) { - encoder = KMSDRM_LEGACY_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]); + encoder = KMSDRM_LEGACY_drmModeGetEncoder(viddata->drm_fd, + resources->encoders[i]); if (!encoder) { continue; } - for (j = 0; j < dispdata->conn->count_encoders; j++) { - if (dispdata->conn->encoders[j] == encoder->encoder_id) { + for (j = 0; j < connector->count_encoders; j++) { + if (connector->encoders[j] == encoder->encoder_id) { break; } } - if (j != dispdata->conn->count_encoders) { + if (j != connector->count_encoders) { break; } @@ -547,156 +599,431 @@ KMSDRM_LEGACY_VideoInit(_THIS) goto cleanup; } - SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id); - /* Try to find a CRTC connected to this encoder */ - dispdata->saved_crtc = KMSDRM_LEGACY_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id); + crtc = KMSDRM_LEGACY_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id); - if (!dispdata->saved_crtc) { - /* No CRTC was connected, find the first CRTC that can be connected */ + /* If no CRTC was connected to the encoder, find the first CRTC + that is supported by the encoder, and use that. */ + if (!crtc) { for (i = 0; i < resources->count_crtcs; i++) { if (encoder->possible_crtcs & (1 << i)) { encoder->crtc_id = resources->crtcs[i]; - dispdata->saved_crtc = KMSDRM_LEGACY_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id); + crtc_index = i; + crtc = KMSDRM_LEGACY_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id); break; } } } - if (!dispdata->saved_crtc) { + if (!crtc) { ret = SDL_SetError("No CRTC found."); goto cleanup; } - SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Saved crtc_id %u, fb_id %u, (%u,%u), %ux%u", - dispdata->saved_crtc->crtc_id, dispdata->saved_crtc->buffer_id, dispdata->saved_crtc->x, - dispdata->saved_crtc->y, dispdata->saved_crtc->width, dispdata->saved_crtc->height); + /* Figure out the default mode to be set. */ + dispdata->mode = crtc->mode; - dispdata->crtc_id = encoder->crtc_id; + /* Save the original mode for restoration on quit. */ + dispdata->original_mode = dispdata->mode; - /* Figure out the default mode to be set. If the current CRTC's mode isn't - valid, select the first mode supported by the connector - - FIXME find first mode that specifies DRM_MODE_TYPE_PREFERRED */ - dispdata->mode = dispdata->saved_crtc->mode; - - if (dispdata->saved_crtc->mode_valid == 0) { - SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, - "Current mode is invalid, selecting connector's mode #0."); - dispdata->mode = dispdata->conn->modes[0]; + /* Find the connector's preferred mode, to be used in case the current mode + is not valid, or if restoring the current mode fails. */ + for (i = 0; i < connector->count_modes; i++) { + if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) { + dispdata->preferred_mode = connector->modes[i]; + } } - /* Setup the single display that's available */ + /* If the current CRTC's mode isn't valid, select the preferred + mode of the connector. */ + if (crtc->mode_valid == 0) { + dispdata->mode = dispdata->preferred_mode; + } - display.desktop_mode.w = dispdata->mode.hdisplay; - display.desktop_mode.h = dispdata->mode.vdisplay; - display.desktop_mode.refresh_rate = dispdata->mode.vrefresh; -#if 1 - display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888; -#else - /* FIXME */ - drmModeFB *fb = drmModeGetFB(viddata->drm_fd, dispdata->saved_crtc->buffer_id); - display.desktop_mode.format = drmToSDLPixelFormat(fb->bpp, fb->depth); - drmModeFreeFB(fb); -#endif - display.current_mode = display.desktop_mode; - display.driverdata = dispdata; - SDL_AddVideoDisplay(&display, SDL_FALSE); + if (dispdata->mode.hdisplay == 0 || dispdata->mode.vdisplay == 0 ) { + ret = SDL_SetError("Couldn't get a valid connector videomode."); + goto cleanup; + } -#ifdef SDL_INPUT_LINUXEV - SDL_EVDEV_Init(); -#endif + /*******************************************************/ + /* Look for a plane that can be connected to our CRTC. */ + /*******************************************************/ + plane_resources = KMSDRM_LEGACY_drmModeGetPlaneResources(viddata->drm_fd); - KMSDRM_LEGACY_InitMouse(_this); + for (int i = 0; (i < plane_resources->count_planes); i++) { - return ret; + drmModePlane *plane = KMSDRM_LEGACY_drmModeGetPlane(viddata->drm_fd, + plane_resources->planes[i]); + + /* 1 - Does this plane support our CRTC? + 2 - Is this plane unused or used by our CRTC? Both possibilities are good. + We don't mind if it's primary or overlay. */ + if ((plane->possible_crtcs & (1 << crtc_index)) && + (plane->crtc_id == crtc->crtc_id || plane->crtc_id == 0 )) + { + dispdata->plane_id = plane->plane_id; + break; + } + + KMSDRM_LEGACY_drmModeFreePlane(plane); + } + + KMSDRM_LEGACY_drmModeFreePlaneResources(plane_resources); + + if (!dispdata->plane_id) { + ret = SDL_SetError("Could not locate a primary plane compatible with active CRTC."); + goto cleanup; + } + + /* Store the connector and crtc for future use. These and the plane_id is + all we keep from this function, and these are just structs, inoffensive to VK. */ + dispdata->connector = connector; + dispdata->crtc = crtc; + + /***********************************/ + /* Block for Vulkan compatibility. */ + /***********************************/ + + /* THIS IS FOR VULKAN! Leave the FD closed, so VK can work. + Will reopen this in CreateWindow, but only if requested a non-VK window. */ + close (viddata->drm_fd); + viddata->drm_fd = -1; cleanup: if (encoder) KMSDRM_LEGACY_drmModeFreeEncoder(encoder); if (resources) KMSDRM_LEGACY_drmModeFreeResources(resources); - - if (ret != 0) { + if (ret) { /* Error (complete) cleanup */ - if (dispdata->conn) { - KMSDRM_LEGACY_drmModeFreeConnector(dispdata->conn); - dispdata->conn = NULL; + if (dispdata->connector) { + KMSDRM_LEGACY_drmModeFreeConnector(dispdata->connector); + dispdata->connector = NULL; } - if (dispdata->saved_crtc) { - KMSDRM_LEGACY_drmModeFreeCrtc(dispdata->saved_crtc); - dispdata->saved_crtc = NULL; - } - if (viddata->gbm) { - KMSDRM_LEGACY_gbm_device_destroy(viddata->gbm); - viddata->gbm = NULL; + if (dispdata->crtc) { + KMSDRM_LEGACY_drmModeFreeCrtc(dispdata->crtc); + dispdata->crtc = NULL; } if (viddata->drm_fd >= 0) { close(viddata->drm_fd); viddata->drm_fd = -1; } - SDL_free(dispdata); } + return ret; } +/*UNUSED function. Keep for documentation purposes. */ +SDL_bool KMSDRM_IsPlanePrimary (_THIS, drmModePlane *plane) { + + SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata; + SDL_bool ret = SDL_FALSE; + + /* Find out if it's a primary plane. */ + drmModeObjectProperties *plane_props = + KMSDRM_LEGACY_drmModeObjectGetProperties(viddata->drm_fd, + plane->plane_id, DRM_MODE_OBJECT_ANY); + + for (int j = 0; (j < plane_props->count_props); j++) { + + drmModePropertyRes *prop = KMSDRM_LEGACY_drmModeGetProperty(viddata->drm_fd, + plane_props->props[j]); + + if ((strcmp(prop->name, "type") == 0) && + (plane_props->prop_values[j] == DRM_PLANE_TYPE_PRIMARY)) + { + ret = SDL_TRUE; + } + + KMSDRM_LEGACY_drmModeFreeProperty(prop); + + } + + KMSDRM_LEGACY_drmModeFreeObjectProperties(plane_props); + + return ret; +} + +/* Init the Vulkan-INCOMPATIBLE stuff: + Reopen FD, create gbm dev, create dumb buffer and setup display plane. + This is to be called late, in WindowCreate(), and ONLY if this is not + a Vulkan window. + We are doing this so late to allow Vulkan to work if we build a VK window. + These things are incompatible with Vulkan, which accesses the same resources + internally so they must be free when trying to build a Vulkan surface. +*/ +int +KMSDRM_LEGACY_GBMInit (_THIS, SDL_DisplayData *dispdata) +{ + SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata; + int ret = 0; + + /* Reopen the FD! */ + viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC); + + /* Create the GBM device. */ + viddata->gbm_dev = KMSDRM_LEGACY_gbm_create_device(viddata->drm_fd); + if (!viddata->gbm_dev) { + ret = SDL_SetError("Couldn't create gbm device."); + } + + dispdata->gbm_init = SDL_TRUE; + + return ret; +} + +/* Deinit the Vulkan-incompatible KMSDRM stuff. */ +void +KMSDRM_LEGACY_GBMDeinit (_THIS, SDL_DisplayData *dispdata) +{ + SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); + + /* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(), + already called when we get here. */ + if (viddata->gbm_dev) { + KMSDRM_LEGACY_gbm_device_destroy(viddata->gbm_dev); + viddata->gbm_dev = NULL; + } + + /* Finally close DRM FD. May be reopen on next non-vulkan window creation. */ + if (viddata->drm_fd >= 0) { + close(viddata->drm_fd); + viddata->drm_fd = -1; + } + + dispdata->gbm_init = SDL_FALSE; +} + +void +KMSDRM_LEGACY_DestroySurfaces(_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; + int ret; + + /**********************************************/ + /* Wait for last issued pageflip to complete. */ + /**********************************************/ + KMSDRM_LEGACY_WaitPageFlip(_this, windata, -1); + + /***********************************************************************/ + /* Restore the original CRTC configuration: configue the crtc with the */ + /* original video mode and make it point to the original TTY buffer. */ + /***********************************************************************/ + + ret = KMSDRM_LEGACY_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id, + dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1, + &dispdata->original_mode); + + /* If we failed to set the original mode, try to set the connector prefered mode. */ + if (ret && (dispdata->crtc->mode_valid == 0)) { + ret = KMSDRM_LEGACY_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id, + dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1, + &dispdata->original_mode); + } + + if(ret) { + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not restore CRTC"); + } + + /***************************/ + /* Destroy the EGL surface */ + /***************************/ + + 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; + } + + /***************************/ + /* Destroy the GBM buffers */ + /***************************/ + + if (windata->bo) { + KMSDRM_LEGACY_gbm_surface_release_buffer(windata->gs, windata->bo); + windata->bo = NULL; + } + + if (windata->next_bo) { + KMSDRM_LEGACY_gbm_surface_release_buffer(windata->gs, windata->next_bo); + windata->next_bo = NULL; + } + + /***************************/ + /* Destroy the GBM surface */ + /***************************/ + + if (windata->gs) { + KMSDRM_LEGACY_gbm_surface_destroy(windata->gs); + windata->gs = NULL; + } +} + +int +KMSDRM_LEGACY_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; + + EGLContext egl_context; + + int ret = 0; + + /* If the current window already has surfaces, destroy them before creating other. + This is mainly for ReconfigureWindow, where we simply call CreateSurfaces() + for regenerating a window's surfaces. */ + if (windata->gs) { + KMSDRM_LEGACY_DestroySurfaces(_this, window); + } + + if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) || + ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) { + + width = dispdata->mode.hdisplay; + height = dispdata->mode.vdisplay; + } else { + width = window->w; + height = window->h; + } + + if (!KMSDRM_LEGACY_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_LEGACY_gbm_surface_create(viddata->gbm_dev, + width, height, surface_fmt, surface_flags); + + if (!windata->gs) { + return SDL_SetError("Could not create GBM surface"); + } + + /* 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) { + ret = SDL_SetError("Could not create EGL window surface"); + goto cleanup; + } + + /* Current context passing to EGL is now done here. If something fails, + go back to delayed SDL_EGL_MakeCurrent() call in SwapWindow. */ + egl_context = (EGLContext)SDL_GL_GetCurrentContext(); + ret = SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context); + +cleanup: + + if (ret) { + /* Error (complete) cleanup. */ + if (windata->gs) { + KMSDRM_LEGACY_gbm_surface_destroy(windata->gs); + windata->gs = NULL; + } + } + + return ret; +} + +int +KMSDRM_LEGACY_VideoInit(_THIS) +{ + int ret = 0; + + SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); + SDL_DisplayData *dispdata = NULL; + SDL_VideoDisplay display = {0}; + + SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()"); + + viddata->video_init = SDL_FALSE; + + dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData)); + if (!dispdata) { + return SDL_OutOfMemory(); + } + + /* Get KMSDRM resources info and store what we need. Getting and storing + this info isn't a problem for VK compatibility. + For VK-incompatible initializations we have KMSDRM_LEGACY_GBMInit(), which is + called on window creation, and only when we know it's not a VK window. */ + if (KMSDRM_LEGACY_DisplayDataInit(_this, dispdata)) { + ret = SDL_SetError("error getting KMS/DRM information"); + goto cleanup; + } + + /* Setup the single display that's available. + There's no problem with it being still incomplete. */ + display.driverdata = dispdata; + display.desktop_mode.w = dispdata->mode.hdisplay; + display.desktop_mode.h = dispdata->mode.vdisplay; + display.desktop_mode.refresh_rate = dispdata->mode.vrefresh; + display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888; + display.current_mode = display.desktop_mode; + + /* Add the display only when it's ready, */ + SDL_AddVideoDisplay(&display, SDL_FALSE); + +#ifdef SDL_INPUT_LINUXEV + SDL_EVDEV_Init(); +#endif + + viddata->video_init = SDL_TRUE; + +cleanup: + + if (ret) { + /* Error (complete) cleanup */ + if (dispdata->crtc) { + SDL_free(dispdata->crtc); + } + if (dispdata->connector) { + SDL_free(dispdata->connector); + } + + SDL_free(dispdata); + } + + return ret; +} + +/* The driverdata pointers, like dispdata, viddata, windata, etc... + are freed by SDL internals, so not our job. */ void KMSDRM_LEGACY_VideoQuit(_THIS) { SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); - SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_LEGACY_VideoQuit()"); + KMSDRM_LEGACY_DisplayDataDeinit(_this, dispdata); - if (_this->gl_config.driver_loaded) { - SDL_GL_UnloadLibrary(); - } +#ifdef SDL_INPUT_LINUXEV + SDL_EVDEV_Quit(); +#endif /* Clear out the window list */ SDL_free(viddata->windows); viddata->windows = NULL; viddata->max_windows = 0; viddata->num_windows = 0; - - /* Restore saved CRTC settings */ - if (viddata->drm_fd >= 0 && dispdata && dispdata->conn && dispdata->saved_crtc) { - drmModeConnector *conn = dispdata->conn; - drmModeCrtc *crtc = dispdata->saved_crtc; - - int ret = KMSDRM_LEGACY_drmModeSetCrtc(viddata->drm_fd, crtc->crtc_id, crtc->buffer_id, - crtc->x, crtc->y, &conn->connector_id, 1, &crtc->mode); - - if (ret != 0) { - SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not restore original CRTC mode"); - } - } - if (dispdata && dispdata->conn) { - KMSDRM_LEGACY_drmModeFreeConnector(dispdata->conn); - dispdata->conn = NULL; - } - if (dispdata && dispdata->saved_crtc) { - KMSDRM_LEGACY_drmModeFreeCrtc(dispdata->saved_crtc); - dispdata->saved_crtc = NULL; - } - if (viddata->gbm) { - KMSDRM_LEGACY_gbm_device_destroy(viddata->gbm); - viddata->gbm = NULL; - } - if (viddata->drm_fd >= 0) { - close(viddata->drm_fd); - SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Closed DRM FD %d", viddata->drm_fd); - viddata->drm_fd = -1; - } -#ifdef SDL_INPUT_LINUXEV - SDL_EVDEV_Quit(); -#endif + viddata->video_init = SDL_FALSE; } void KMSDRM_LEGACY_GetDisplayModes(_THIS, SDL_VideoDisplay * display) { SDL_DisplayData *dispdata = display->driverdata; - drmModeConnector *conn = dispdata->conn; + drmModeConnector *conn = dispdata->connector; SDL_DisplayMode mode; int i; @@ -725,7 +1052,7 @@ KMSDRM_LEGACY_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata; SDL_DisplayData *dispdata = (SDL_DisplayData *)display->driverdata; SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata; - drmModeConnector *conn = dispdata->conn; + drmModeConnector *conn = dispdata->connector; int i; if (!modedata) { @@ -749,99 +1076,52 @@ KMSDRM_LEGACY_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode return 0; } -int -KMSDRM_LEGACY_CreateWindow(_THIS, SDL_Window * window) -{ - SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata; - SDL_WindowData *windata; - SDL_VideoDisplay *display; - - if (!_this->egl_data) { - if (SDL_GL_LoadLibrary(NULL) < 0) { - goto error; - } - } - - /* Allocate window internal data */ - windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData)); - - if (!windata) { - SDL_OutOfMemory(); - goto error; - } - - /* Windows have one size for now */ - display = SDL_GetDisplayForWindow(window); - window->w = display->desktop_mode.w; - window->h = display->desktop_mode.h; - - /* Maybe you didn't ask for a fullscreen OpenGL window, but that's what you get */ - window->flags |= (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL); - - /* In case we want low-latency, double-buffer video, we take note here */ - windata->double_buffer = SDL_FALSE; - - if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) { - windata->double_buffer = SDL_TRUE; - } - - /* Setup driver data for this window */ - windata->viddata = viddata; - window->driverdata = windata; - - if (KMSDRM_LEGACY_CreateSurfaces(_this, window)) { - goto error; - } - - /* Add window to the internal list of tracked windows. Note, while it may - seem odd to support multiple fullscreen windows, some apps create an - extra window as a dummy surface when working with multiple contexts */ - if (viddata->num_windows >= viddata->max_windows) { - int new_max_windows = viddata->max_windows + 1; - viddata->windows = (SDL_Window **)SDL_realloc(viddata->windows, - new_max_windows * sizeof(SDL_Window *)); - viddata->max_windows = new_max_windows; - - if (!viddata->windows) { - SDL_OutOfMemory(); - goto error; - } - } - - viddata->windows[viddata->num_windows++] = window; - - /* Focus on the newly created window */ - SDL_SetMouseFocus(window); - SDL_SetKeyboardFocus(window); - - return 0; - -error: - KMSDRM_LEGACY_DestroyWindow(_this, window); - - return -1; -} - void -KMSDRM_LEGACY_DestroyWindow(_THIS, SDL_Window * window) +KMSDRM_LEGACY_DestroyWindow(_THIS, SDL_Window *window) { SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; - SDL_VideoData *viddata; - int i, j; + SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; + SDL_VideoData *viddata = windata->viddata; + SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */ + unsigned int i, j; if (!windata) { return; } - viddata = windata->viddata; + if ( !is_vulkan && dispdata->gbm_init ) { - /* If this is the only window left, hide the cursor. */ - if (viddata->num_windows == 1) - { - SDL_ShowCursor(SDL_FALSE); + /* Destroy cursor GBM plane. */ + KMSDRM_LEGACY_DeinitMouse(_this); + + /* Destroy GBM surface and buffers. */ + KMSDRM_LEGACY_DestroySurfaces(_this, window); + + /* Unload EGL library. */ + if (_this->egl_data) { + SDL_EGL_UnloadLibrary(_this); + } + + /* Unload GL library. */ + if (_this->gl_config.driver_loaded) { + SDL_GL_UnloadLibrary(); + } + + /* Free display plane, and destroy GBM device. */ + KMSDRM_LEGACY_GBMDeinit(_this, dispdata); } - /* Remove from the internal window list */ + else { + /* If we were in Vulkan mode, get out of it. */ + if (viddata->vulkan_mode) { + viddata->vulkan_mode = SDL_FALSE; + } + } + + /********************************************/ + /* Remove from the internal SDL window list */ + /********************************************/ + for (i = 0; i < viddata->num_windows; i++) { if (viddata->windows[i] == window) { viddata->num_windows--; @@ -854,13 +1134,198 @@ KMSDRM_LEGACY_DestroyWindow(_THIS, SDL_Window * window) } } - KMSDRM_LEGACY_DestroySurfaces(_this, window); - + /*********************************************************************/ + /* Free the window driverdata. Bye bye, surface and buffer pointers! */ + /*********************************************************************/ window->driverdata = NULL; - SDL_free(windata); } +int +KMSDRM_LEGACY_CreateWindow(_THIS, SDL_Window * window) +{ + SDL_WindowData *windata = NULL; + SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata; + SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); + SDL_DisplayData *dispdata = display->driverdata; + SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */ + SDL_bool vulkan_mode = viddata->vulkan_mode; /* Do we have any Vulkan windows? */ + NativeDisplayType egl_display; + float ratio; + int ret = 0; + + if ( !(dispdata->gbm_init) && !is_vulkan && !vulkan_mode ) { + + /* If this is not a Vulkan Window, then this is a GL window, so at the + end of this function, we must have marked the window as being OPENGL + and we must have loaded the GL library: both things are needed so the + GL_CreateRenderer() and GL_LoadFunctions() calls in SDL_CreateWindow() + succeed without having to re-create the window. + We must load the EGL library too, which can't be loaded until the GBM + device has been created, because SDL_EGL_Library() function uses it. */ + + /* Maybe you didn't ask for an OPENGL window, but that's what you will get. + See previous comment on why. */ + window->flags |= SDL_WINDOW_OPENGL; + + /* Reopen FD, create gbm dev, setup display plane, etc,. + but only when we come here for the first time, + and only if it's not a VK window. */ + if ((ret = KMSDRM_LEGACY_GBMInit(_this, dispdata))) { + goto cleanup; + } + + /* Manually load the GL library. KMSDRM_EGL_LoadLibrary() has already + been called by SDL_CreateWindow() but we don't do anything there, + precisely to be able to load it here. + If we let SDL_CreateWindow() load the lib, it will be loaded + before we call KMSDRM_GBMInit(), causing GLES programs to fail. */ + if (!_this->egl_data) { + egl_display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm_dev; + if (SDL_EGL_LoadLibrary(_this, NULL, egl_display, EGL_PLATFORM_GBM_MESA)) { + goto cleanup; + } + + if (SDL_GL_LoadLibrary(NULL) < 0) { + goto cleanup; + } + } + + /* Can't init mouse stuff sooner because cursor plane is not ready, + so we do it here. */ + KMSDRM_LEGACY_InitMouse(_this); + + /* Since we take cursor buffer way from the cursor plane and + destroy the cursor GBM BO when we destroy a window, we must + also manually re-show the cursor on screen, if necessary, + when we create a window. */ + KMSDRM_LEGACY_InitCursor(); + } + + /* Allocate window internal data */ + windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData)); + if (!windata) { + ret = SDL_OutOfMemory(); + goto cleanup; + } + + if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) + || ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) + { + windata->src_w = dispdata->mode.hdisplay; + windata->src_h = dispdata->mode.vdisplay; + windata->output_w = dispdata->mode.hdisplay; + windata->output_h = dispdata->mode.vdisplay; + windata->output_x = 0; + } else { + /* Normal non-fullscreen windows are scaled to the in-use video mode + using a PLANE connected to the CRTC, so get input size, + output (CRTC) size, and position. */ + ratio = (float)window->w / (float)window->h; + windata->src_w = window->w; + windata->src_h = window->h; + windata->output_w = dispdata->mode.vdisplay * ratio; + windata->output_h = dispdata->mode.vdisplay; + windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2; + } + + /* Don't force fullscreen on all windows: it confuses programs that try + to set a window fullscreen after creating it as non-fullscreen (sm64ex) */ + // window->flags |= SDL_WINDOW_FULLSCREEN; + + /* Setup driver data for this window */ + windata->viddata = viddata; + window->driverdata = windata; + + if (!is_vulkan && !vulkan_mode) { + /* Create the window surfaces. Needs the window diverdata in place. */ + if ((ret = KMSDRM_LEGACY_CreateSurfaces(_this, window))) { + goto cleanup; + } + } + + /* Add window to the internal list of tracked windows. Note, while it may + seem odd to support multiple fullscreen windows, some apps create an + extra window as a dummy surface when working with multiple contexts */ + if (viddata->num_windows >= viddata->max_windows) { + unsigned int new_max_windows = viddata->max_windows + 1; + viddata->windows = (SDL_Window **)SDL_realloc(viddata->windows, + new_max_windows * sizeof(SDL_Window *)); + viddata->max_windows = new_max_windows; + + if (!viddata->windows) { + ret = SDL_OutOfMemory(); + goto cleanup; + } + } + + viddata->windows[viddata->num_windows++] = window; + + /* If we have just created a Vulkan window, establish that we are in Vulkan mode now. */ + viddata->vulkan_mode = is_vulkan; + + /* Focus on the newly created window */ + SDL_SetMouseFocus(window); + SDL_SetKeyboardFocus(window); + + /***********************************************************/ + /* Tell SDL that the mouse has entered the window using an */ + /* artificial event: we have no windowing system to tell */ + /* SDL that it has happened. This makes SDL set the */ + /* SDL_WINDOW_MOUSE_FOCUS on this window, thus fixing */ + /* Scummvm sticky-on-sides software cursor. */ + /***********************************************************/ + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_ENTER, 0, 0); + +cleanup: + + if (ret) { + /* Allocated windata will be freed in KMSDRM_DestroyWindow */ + KMSDRM_LEGACY_DestroyWindow(_this, window); + } + return ret; +} + +/*****************************************************************************/ +/* Reconfigure the window scaling parameters and re-construct it's surfaces, */ +/* without destroying the window itself. */ +/* To be used by SetWindowSize() and SetWindowFullscreen(). */ +/*****************************************************************************/ +static int +KMSDRM_LEGACY_ReconfigureWindow( _THIS, SDL_Window * window) { + SDL_WindowData *windata = window->driverdata; + SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; + SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */ + float ratio; + + if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) || + ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) { + + windata->src_w = dispdata->mode.hdisplay; + windata->src_h = dispdata->mode.vdisplay; + windata->output_w = dispdata->mode.hdisplay; + windata->output_h = dispdata->mode.vdisplay; + windata->output_x = 0; + + } else { + /* Normal non-fullscreen windows are scaled using the CRTC, + so get output (CRTC) size and position, for AR correction. */ + ratio = (float)window->w / (float)window->h; + windata->src_w = window->w; + windata->src_h = window->h; + windata->output_w = dispdata->mode.vdisplay * ratio; + windata->output_h = dispdata->mode.vdisplay; + windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2; + } + + if (!is_vulkan) { + if (KMSDRM_LEGACY_CreateSurfaces(_this, window)) { + return -1; + } + } + return 0; +} + int KMSDRM_LEGACY_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) { @@ -882,6 +1347,16 @@ KMSDRM_LEGACY_SetWindowPosition(_THIS, SDL_Window * window) void KMSDRM_LEGACY_SetWindowSize(_THIS, SDL_Window * window) { + if (KMSDRM_LEGACY_ReconfigureWindow(_this, window)) { + SDL_SetError("Can't reconfigure window on SetWindowSize."); + } +} +void +KMSDRM_LEGACY_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) +{ + if (KMSDRM_LEGACY_ReconfigureWindow(_this, window)) { + SDL_SetError("Can't reconfigure window on SetWindowFullscreen."); + } } void KMSDRM_LEGACY_ShowWindow(_THIS, SDL_Window * window) diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.h b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.h index 53fdb1bac..53a30eab4 100644 --- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.h +++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.h @@ -37,7 +37,12 @@ typedef struct SDL_VideoData { int devindex; /* device index that was passed on creation */ int drm_fd; /* DRM file desc */ - struct gbm_device *gbm; + char devpath[32]; /* DRM dev path. */ + + struct gbm_device *gbm_dev; + + SDL_bool video_init; /* Has VideoInit succeeded? */ + SDL_bool vulkan_mode; /* Are we in Vulkan mode? One VK window is enough to be. */ SDL_Window **windows; int max_windows; @@ -53,26 +58,53 @@ typedef struct SDL_DisplayModeData typedef struct SDL_DisplayData { - uint32_t crtc_id; - drmModeConnector *conn; + drmModeConnector *connector; + drmModeCrtc *crtc; drmModeModeInfo mode; - drmModeCrtc *saved_crtc; /* CRTC to restore on quit */ -} SDL_DisplayData; + drmModeModeInfo original_mode; + drmModeModeInfo preferred_mode; + drmModeCrtc *saved_crtc; /* CRTC to restore on quit */ + + uint32_t plane_id; /* ID of the primary plane used by the CRTC */ + + SDL_bool gbm_init; + + /* DRM & GBM cursor stuff lives here, not in an SDL_Cursor's driverdata struct, + because setting/unsetting up these is done on window creation/destruction, + where we may not have an SDL_Cursor at all (so no SDL_Cursor driverdata). + There's only one cursor GBM BO because we only support one cursor. */ + struct gbm_bo *cursor_bo; + uint64_t cursor_w, cursor_h; + + SDL_bool modeset_pending; + +} SDL_DisplayData; typedef struct SDL_WindowData { SDL_VideoData *viddata; + /* SDL internals expect EGL surface to be here, and in KMSDRM the GBM surface is + what supports the EGL surface on the driver side, so all these surfaces and buffers + are expected to be here, in the struct pointed by SDL_Window driverdata pointer: + this one. So don't try to move these to dispdata! */ struct gbm_surface *gs; - struct gbm_bo *curr_bo; + struct gbm_bo *bo; struct gbm_bo *next_bo; - struct gbm_bo *crtc_bo; + SDL_bool waiting_for_flip; SDL_bool double_buffer; int egl_surface_dirty; EGLSurface egl_surface; + /* For scaling and AR correction. */ + int32_t src_w; + int32_t src_h; + int32_t output_w; + int32_t output_h; + int32_t output_x; + } SDL_WindowData; typedef struct KMSDRM_LEGACY_FBInfo @@ -101,6 +133,7 @@ void KMSDRM_LEGACY_SetWindowTitle(_THIS, SDL_Window * window); void KMSDRM_LEGACY_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon); void KMSDRM_LEGACY_SetWindowPosition(_THIS, SDL_Window * window); void KMSDRM_LEGACY_SetWindowSize(_THIS, SDL_Window * window); +void KMSDRM_LEGACY_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen); void KMSDRM_LEGACY_ShowWindow(_THIS, SDL_Window * window); void KMSDRM_LEGACY_HideWindow(_THIS, SDL_Window * window); void KMSDRM_LEGACY_RaiseWindow(_THIS, SDL_Window * window);