diff --git a/src/video/kmsdrm/SDL_kmsdrmmouse.c b/src/video/kmsdrm/SDL_kmsdrmmouse.c index a4a5c24e2bf4c..08f7394efdda0 100644 --- a/src/video/kmsdrm/SDL_kmsdrmmouse.c +++ b/src/video/kmsdrm/SDL_kmsdrmmouse.c @@ -67,6 +67,23 @@ void KMSDRM_DestroyCursorBO(SDL_VideoDevice *_this, SDL_VideoDisplay *display) // Destroy the curso GBM BO. if (dispdata->cursor_bo) { + SDL_VideoData *viddata = (SDL_VideoData *) _this->internal; + if (viddata->is_atomic) { + if (dispdata->cursor_plane) { + // Unset the the cursor BO from the cursor plane. + KMSDRM_PlaneInfo info; + SDL_zero(info); + info.plane = dispdata->cursor_plane; + drm_atomic_set_plane_props(&info); + // Wait until the cursor is unset from the cursor plane before destroying it's BO. + if (drm_atomic_commit(_this, true, false)) { + SDL_SetError("Failed atomic commit in KMSDRM_DenitMouse."); + } + // Free the cursor plane, on which the cursor was being shown. + free_plane(&dispdata->cursor_plane); + } + } + KMSDRM_gbm_bo_destroy(dispdata->cursor_bo); dispdata->cursor_bo = NULL; dispdata->cursor_bo_drm_fd = -1; @@ -78,11 +95,14 @@ void KMSDRM_DestroyCursorBO(SDL_VideoDevice *_this, SDL_VideoDisplay *display) build a window and assign a display to it. */ bool KMSDRM_CreateCursorBO(SDL_VideoDisplay *display) { - SDL_VideoDevice *dev = SDL_GetVideoDevice(); SDL_VideoData *viddata = dev->internal; SDL_DisplayData *dispdata = display->internal; + if (viddata->is_atomic) { + setup_plane(dev, &dispdata->cursor_plane, DRM_PLANE_TYPE_CURSOR); + } + if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) { @@ -121,15 +141,29 @@ static bool KMSDRM_RemoveCursorFromBO(SDL_VideoDisplay *display) SDL_VideoDevice *video_device = SDL_GetVideoDevice(); SDL_VideoData *viddata = video_device->internal; - const int rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id, 0, 0, 0); - if (rc < 0) { - result = SDL_SetError("drmModeSetCursor() failed: %s", strerror(-rc)); + if (viddata->is_atomic) { + if (dispdata->cursor_plane) { + KMSDRM_PlaneInfo info; + SDL_zero(info); + info.plane = dispdata->cursor_plane; + // The rest of the members are zeroed, so this takes away the cursor from the cursor plane. + drm_atomic_set_plane_props(&info); + if (drm_atomic_commit(video_device, true, false)) { + result = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor."); + } + } + } else { + const int rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, 0, 0, 0); + if (rc < 0) { + result = SDL_SetError("drmModeSetCursor() failed: %s", strerror(-rc)); + } } + return result; } // Dump a cursor buffer to a display's DRM cursor BO. -static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Cursor *cursor) +static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Mouse *mouse, SDL_Cursor *cursor) { SDL_DisplayData *dispdata = display->internal; SDL_CursorData *curdata = cursor->internal; @@ -173,22 +207,42 @@ static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Cursor *cursor) goto cleanup; } - // Put the GBM BO buffer on screen using the DRM interface. - bo_handle = KMSDRM_gbm_bo_get_handle(dispdata->cursor_bo).u32; - if (curdata->hot_x == 0 && curdata->hot_y == 0) { - rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id, - bo_handle, dispdata->cursor_w, dispdata->cursor_h); + if (viddata->is_atomic) { + // Get the fb_id for the GBM BO so we can show it on the cursor plane. + KMSDRM_FBInfo *fb = KMSDRM_FBFromBO(video_device, dispdata->cursor_bo); + KMSDRM_PlaneInfo info; + + // Show the GBM BO buffer on the cursor plane. + SDL_zero(info); + info.plane = dispdata->cursor_plane; + info.crtc_id = dispdata->crtc.crtc->crtc_id; + info.fb_id = fb->fb_id; + info.src_w = dispdata->cursor_w; + info.src_h = dispdata->cursor_h; + info.crtc_x = ((int32_t) SDL_roundf(mouse->x)) - curdata->hot_x; + info.crtc_y = ((int32_t) SDL_roundf(mouse->y)) - curdata->hot_y; + info.crtc_w = curdata->w; + info.crtc_h = curdata->h; + drm_atomic_set_plane_props(&info); + if (drm_atomic_commit(video_device, true, false)) { + result = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor."); + goto cleanup; + } } else { - rc = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc->crtc_id, - bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y); - } - if (rc < 0) { - result = SDL_SetError("Failed to set DRM cursor: %s", strerror(-rc)); - goto cleanup; + // Put the GBM BO buffer on screen using the DRM interface. + bo_handle = KMSDRM_gbm_bo_get_handle(dispdata->cursor_bo).u32; + if (curdata->hot_x == 0 && curdata->hot_y == 0) { + rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, bo_handle, dispdata->cursor_w, dispdata->cursor_h); + } else { + rc = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y); + } + if (rc < 0) { + result = SDL_SetError("Failed to set DRM cursor: %s", strerror(-rc)); + goto cleanup; + } } cleanup: - if (ready_buffer) { SDL_free(ready_buffer); } @@ -316,7 +370,7 @@ static bool KMSDRM_ShowCursor(SDL_Cursor *cursor) if (cursor) { /* Dump the cursor to the display DRM cursor BO so it becomes visible on that display. */ - result = KMSDRM_DumpCursorToBO(display, cursor); + result = KMSDRM_DumpCursorToBO(display, mouse, cursor); } else { // Hide the cursor on that display. result = KMSDRM_RemoveCursorFromBO(display); @@ -327,6 +381,19 @@ static bool KMSDRM_ShowCursor(SDL_Cursor *cursor) return result; } +static void drm_atomic_movecursor(const SDL_CursorData *curdata, uint16_t x, uint16_t y) +{ + SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); + if (dispdata->cursor_plane) { // We can't move a non-existing cursor, but that's ok. + // Do we have a set of changes already in the making? If not, allocate a new one. + if (!dispdata->atomic_req) { + dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc(); + } + add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_X", x - curdata->hot_x); + add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_Y", y - curdata->hot_y); + } +} + static bool KMSDRM_WarpMouseGlobal(float x, float y) { SDL_Mouse *mouse = SDL_GetMouse(); @@ -340,17 +407,25 @@ static bool KMSDRM_WarpMouseGlobal(float x, float y) // And now update the cursor graphic position on screen. if (dispdata->cursor_bo) { - const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc->crtc_id, (int)x, (int)y); - if (rc < 0) { - return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc)); + SDL_VideoDevice *dev = SDL_GetVideoDevice(); + SDL_VideoData *viddata = dev->internal; + if (viddata->is_atomic) { + const SDL_CursorData *curdata = (const SDL_CursorData *) mouse->cur_cursor->internal; + drm_atomic_movecursor(curdata, (uint16_t) (int) x, (uint16_t) (int) y); + } else { + const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc.crtc->crtc_id, (int)x, (int)y); + if (rc < 0) { + return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc)); + } } - return true; } else { return SDL_SetError("Cursor not initialized properly."); } } else { return SDL_SetError("No mouse or current cursor."); } + + return true; } static bool KMSDRM_WarpMouse(SDL_Window *window, float x, float y) @@ -394,14 +469,27 @@ static bool KMSDRM_MoveCursor(SDL_Cursor *cursor) if (mouse && mouse->cur_cursor && mouse->focus) { SDL_Window *window = mouse->focus; SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window); + SDL_VideoDevice *dev = SDL_GetVideoDevice(); + SDL_VideoData *viddata = dev->internal; if (!dispdata->cursor_bo) { return SDL_SetError("Cursor not initialized properly."); } - const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc->crtc_id, (int)mouse->x, (int)mouse->y); - if (rc < 0) { - return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc)); + if (viddata->is_atomic) { + /* !!! FIXME: Some programs expect cursor movement even while they don't do SwapWindow() calls, + and since we ride on the atomic_commit() in SwapWindow() for cursor movement, + cursor won't move in these situations. We could do an atomic_commit() here + for each cursor movement request, but it cripples the movement to 30FPS, + so a future solution is needed. SDLPoP "QUIT?" menu is an example of this + situation. */ + const SDL_CursorData *curdata = (const SDL_CursorData *) mouse->cur_cursor->internal; + drm_atomic_movecursor(curdata, (uint16_t) (int) mouse->x, (uint16_t) (int) mouse->y); + } else { + const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc.crtc->crtc_id, (int)mouse->x, (int)mouse->y); + if (rc < 0) { + return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc)); + } } } return true; diff --git a/src/video/kmsdrm/SDL_kmsdrmopengles.c b/src/video/kmsdrm/SDL_kmsdrmopengles.c index 8f15de86c2f73..cfddd5c07dc7c 100644 --- a/src/video/kmsdrm/SDL_kmsdrmopengles.c +++ b/src/video/kmsdrm/SDL_kmsdrmopengles.c @@ -28,10 +28,25 @@ #include "SDL_kmsdrmdyn.h" #include +#define VOID2U64(x) ((uint64_t)(size_t)(x)) + #ifndef EGL_PLATFORM_GBM_MESA #define EGL_PLATFORM_GBM_MESA 0x31D7 #endif +#ifndef EGL_SYNC_NATIVE_FENCE_ANDROID +#define EGL_SYNC_NATIVE_FENCE_ANDROID 0x3144 +#endif + +#ifndef EGL_SYNC_NATIVE_FENCE_FD_ANDROID +#define EGL_SYNC_NATIVE_FENCE_FD_ANDROID 0x3145 +#endif + +#ifndef EGL_NO_NATIVE_FENCE_FD_ANDROID +#define EGL_NO_NATIVE_FENCE_FD_ANDROID -1 +#endif + + // EGL implementation of SDL OpenGL support void KMSDRM_GLES_DefaultProfileConfig(SDL_VideoDevice *_this, int *mask, int *major, int *minor) @@ -83,7 +98,263 @@ bool KMSDRM_GLES_SetSwapInterval(SDL_VideoDevice *_this, int interval) return true; } -bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) +static EGLSyncKHR create_fence(SDL_VideoDevice *_this, int fd) +{ + EGLint attrib_list[] = { + EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fd, + EGL_NONE, + }; + + EGLSyncKHR fence = _this->egl_data->eglCreateSyncKHR(_this->egl_data->egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID, attrib_list); + + SDL_assert(fence); + return fence; +} + +/***********************************************************************************/ +/* Comments about buffer access protection mechanism (=fences) are the ones boxed. */ +/* Also, DON'T remove the asserts: if a fence-related call fails, it's better that */ +/* program exits immediately, or we could leave KMS waiting for a failed/missing */ +/* fence forever. */ +/***********************************************************************************/ +static bool KMSDRM_GLES_SwapWindowFenced(SDL_VideoDevice *_this, SDL_Window * window) +{ + SDL_WindowData *windata = ((SDL_WindowData *) window->internal); + SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window); + KMSDRM_FBInfo *fb; + KMSDRM_PlaneInfo info; + bool modesetting = false; + + SDL_zero(info); + + /******************************************************************/ + /* Create the GPU-side FENCE OBJECT. It will be inserted into the */ + /* GL CMDSTREAM exactly at the end of the gl commands that form a */ + /* frame.(KMS will have to wait on it before doing a pageflip.) */ + /******************************************************************/ + dispdata->gpu_fence = create_fence(_this, EGL_NO_NATIVE_FENCE_FD_ANDROID); + SDL_assert(dispdata->gpu_fence); + + /******************************************************************/ + /* eglSwapBuffers flushes the fence down the GL CMDSTREAM, so we */ + /* know for sure it's there now. */ + /* Also it marks, at EGL level, the buffer that we want to become */ + /* the new front buffer. (Remember that won't really happen until */ + /* we request a pageflip at the KMS level and it completes. */ + /******************************************************************/ + if (! _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface)) { + return SDL_EGL_SetError("Failed to swap EGL buffers", "eglSwapBuffers"); + } + + /******************************************************************/ + /* EXPORT the GPU-side FENCE OBJECT to the fence INPUT FD, so we */ + /* can pass it into the kernel. Atomic ioctl will pass the */ + /* in-fence fd into the kernel, thus telling KMS that it has to */ + /* wait for GPU to finish rendering the frame (remember where we */ + /* put the fence in the GL CMDSTREAM) before doing the changes */ + /* requested in the atomic ioct (the pageflip in this case). */ + /* (We export the GPU-side FENCE OJECT to the fence INPUT FD now, */ + /* not sooner, because now we are sure that the GPU-side fence is */ + /* in the CMDSTREAM to be lifted when the CMDSTREAM to this point */ + /* is completed). */ + /******************************************************************/ + dispdata->kms_in_fence_fd = _this->egl_data->eglDupNativeFenceFDANDROID (_this->egl_data->egl_display, dispdata->gpu_fence); + + _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->gpu_fence); + SDL_assert(dispdata->kms_in_fence_fd != -1); + + /* Lock the buffer that is marked by eglSwapBuffers() to become the + next front buffer (so it can not be chosen by EGL as back buffer + to draw on), and get a handle to it to request the pageflip on it. + REMEMBER that gbm_surface_lock_front_buffer() ALWAYS has to be + called after eglSwapBuffers(). */ + windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs); + if (!windata->next_bo) { + return SDL_SetError("Failed to lock frontbuffer"); + } + fb = KMSDRM_FBFromBO(_this, windata->next_bo); + if (!fb) { + return SDL_SetError("Failed to get a new framebuffer from BO"); + } + + /* Add the pageflip to the request list. */ + info.plane = dispdata->display_plane; + info.crtc_id = dispdata->crtc.crtc->crtc_id; + info.fb_id = fb->fb_id; + info.src_w = window->w; // !!! FIXME: was windata->src_w in the original atomic patch + info.src_h = window->h; // !!! FIXME: was windata->src_h in the original atomic patch + info.crtc_w = dispdata->mode.hdisplay; // !!! FIXME: was windata->output_w in the original atomic patch + info.crtc_h = dispdata->mode.vdisplay; // !!! FIXME: was windata->output_h in the original atomic patch + info.crtc_x = 0; // !!! FIXME: was windata->output_x in the original atomic patch + + drm_atomic_set_plane_props(&info); + + /*****************************************************************/ + /* Tell the display (KMS) that it will have to wait on the fence */ + /* for the GPU-side FENCE. */ + /* */ + /* Since KMS is a kernel thing, we have to pass an FD into */ + /* the kernel, and get another FD out of the kernel. */ + /* */ + /* 1) To pass the GPU-side fence into the kernel, we set the */ + /* INPUT FD as the IN_FENCE_FD prop of the PRIMARY PLANE. */ + /* This FD tells KMS (the kernel) to wait for the GPU-side fence.*/ + /* */ + /* 2) To get the KMS-side fence out of the kernel, we set the */ + /* OUTPUT FD as the OUT_FEWNCE_FD prop of the CRTC. */ + /* This FD will be later imported as a FENCE OBJECT which will be*/ + /* used to tell the GPU to wait for KMS to complete the changes */ + /* requested in atomic_commit (the pageflip in this case). */ + /*****************************************************************/ + if (dispdata->kms_in_fence_fd != -1) + { + add_plane_property(dispdata->atomic_req, dispdata->display_plane, + "IN_FENCE_FD", dispdata->kms_in_fence_fd); + add_crtc_property(dispdata->atomic_req, &dispdata->crtc, + "OUT_FENCE_PTR", VOID2U64(&dispdata->kms_out_fence_fd)); + } + + /* Do we have a pending modesetting? If so, set the necessary + props so it's included in the incoming atomic commit. */ + if (windata->egl_surface_dirty) { + // !!! FIXME: this CreateSurfaces call is what the legacy path does; it's not clear to me if the atomic paths need to do it too. + KMSDRM_CreateSurfaces(_this, window); + + uint32_t blob_id; + SDL_VideoData *viddata = (SDL_VideoData *)_this->internal; + + add_connector_property(dispdata->atomic_req, &dispdata->connector, "CRTC_ID", dispdata->crtc.crtc->crtc_id); + KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->mode), &blob_id); + add_crtc_property(dispdata->atomic_req, &dispdata->crtc, "MODE_ID", blob_id); + add_crtc_property(dispdata->atomic_req, &dispdata->crtc, "active", 1); + modesetting = true; + } + + /*****************************************************************/ + /* Issue a non-blocking atomic commit: for triple buffering, */ + /* this must not block so the game can start building another */ + /* frame, even if the just-requested pageflip hasnt't completed. */ + /*****************************************************************/ + if (drm_atomic_commit(_this, false, modesetting)) { + return SDL_SetError("Failed to issue atomic commit on pageflip"); + } + + /* Release the previous front buffer so EGL can chose it as back buffer + and render on it again. */ + if (windata->bo) { + KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo); + } + /* Take note of the buffer about to become front buffer, so next + time we come here we can free it like we just did with the previous + front buffer. */ + windata->bo = windata->next_bo; + + /****************************************************************/ + /* Import the KMS-side FENCE OUTPUT FD from the kernel to the */ + /* KMS-side FENCE OBJECT so we can use use it to fence the GPU. */ + /****************************************************************/ + dispdata->kms_fence = create_fence(_this, dispdata->kms_out_fence_fd); + SDL_assert(dispdata->kms_fence); + + /****************************************************************/ + /* "Delete" the fence OUTPUT FD, because we already have the */ + /* KMS FENCE OBJECT, the fence itself is away from us, on the */ + /* kernel side. */ + /****************************************************************/ + dispdata->kms_out_fence_fd = -1; + + /*****************************************************************/ + /* Tell the GPU to wait on the fence for the KMS-side FENCE, */ + /* which means waiting until the requested pageflip is completed.*/ + /*****************************************************************/ + _this->egl_data->eglWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0); + + return true; +} + +static bool KMSDRM_GLES_SwapWindowDoubleBuffered(SDL_VideoDevice *_this, SDL_Window * window) +{ + SDL_WindowData *windata = ((SDL_WindowData *) window->internal); + SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window); + KMSDRM_FBInfo *fb; + KMSDRM_PlaneInfo info; + bool modesetting = false; + + SDL_zero(info); + + /**********************************************************************************/ + /* In double-buffer mode, atomic_commit will always be synchronous/blocking (ie: */ + /* won't return until the requested changes are really done). */ + /* Also, there's no need to fence KMS or the GPU, because we won't be entering */ + /* game loop again (hence not building or executing a new cmdstring) until */ + /* pageflip is done, so we don't need to protect the KMS/GPU access to the buffer.*/ + /**********************************************************************************/ + + /* Mark, at EGL level, the buffer that we want to become the new front buffer. + It won't really happen until we request a pageflip at the KMS level and it + completes. */ + if (! _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface)) { + return SDL_EGL_SetError("Failed to swap EGL buffers", "eglSwapBuffers"); + } + /* Lock the buffer that is marked by eglSwapBuffers() to become the next front buffer + (so it can not be chosen by EGL as back buffer to draw on), and get a handle to it, + to request the pageflip on it. */ + windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs); + if (!windata->next_bo) { + return SDL_SetError("Failed to lock frontbuffer"); + } + fb = KMSDRM_FBFromBO(_this, windata->next_bo); + if (!fb) { + return SDL_SetError("Failed to get a new framebuffer BO"); + } + + /* Add the pageflip to the request list. */ + info.plane = dispdata->display_plane; + info.crtc_id = dispdata->crtc.crtc->crtc_id; + info.fb_id = fb->fb_id; + info.src_w = window->w; // !!! FIXME: was windata->src_w in the original atomic patch + info.src_h = window->h; // !!! FIXME: was windata->src_h in the original atomic patch + info.crtc_w = dispdata->mode.hdisplay; // !!! FIXME: was windata->output_w in the original atomic patch + info.crtc_h = dispdata->mode.vdisplay; // !!! FIXME: was windata->output_h in the original atomic patch + info.crtc_x = 0; // !!! FIXME: was windata->output_x in the original atomic patch + + drm_atomic_set_plane_props(&info); + + /* Do we have a pending modesetting? If so, set the necessary + props so it's included in the incoming atomic commit. */ + if (windata->egl_surface_dirty) { + // !!! FIXME: this CreateSurfaces call is what the legacy path does; it's not clear to me if the atomic paths need to do it too. + KMSDRM_CreateSurfaces(_this, window); + + uint32_t blob_id; + + SDL_VideoData *viddata = (SDL_VideoData *)_this->internal; + + add_connector_property(dispdata->atomic_req, &dispdata->connector, "CRTC_ID", dispdata->crtc.crtc->crtc_id); + KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->mode), &blob_id); + add_crtc_property(dispdata->atomic_req, &dispdata->crtc, "MODE_ID", blob_id); + add_crtc_property(dispdata->atomic_req, &dispdata->crtc, "active", 1); + modesetting = true; + } + + /* Issue the one and only atomic commit where all changes will be requested! + Blocking for double buffering: won't return until completed. */ + if (drm_atomic_commit(_this, true, modesetting)) { + return SDL_SetError("Failed to issue atomic commit on pageflip"); + } + + /* Release last front buffer so EGL can chose it as back buffer and render on it again. */ + if (windata->bo) { + KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo); + } + + /* Take note of current front buffer, so we can free it next time we come here. */ + windata->bo = windata->next_bo; + + return true; +} + +static bool KMSDRM_GLES_SwapWindowLegacy(SDL_VideoDevice *_this, SDL_Window *window) { SDL_WindowData *windata = window->internal; SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window); @@ -116,13 +387,12 @@ bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) // Release the previous front buffer if (windata->bo) { KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo); - windata->bo = NULL; } windata->bo = windata->next_bo; /* Mark a buffer to become the next front buffer. - This won't happen until pagelip completes. */ + This won't happen until pageflip completes. */ if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface))) { return SDL_SetError("eglSwapBuffers failed"); @@ -147,8 +417,8 @@ bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) drmModePageFlip can be used the CRTC has to be configured to use the current connector and mode with drmModeSetCrtc */ ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, - dispdata->crtc->crtc_id, fb_info->fb_id, 0, 0, - &dispdata->connector->connector_id, 1, &dispdata->mode); + dispdata->crtc.crtc->crtc_id, fb_info->fb_id, 0, 0, + &dispdata->connector.connector->connector_id, 1, &dispdata->mode); if (ret) { return SDL_SetError("Could not set videomode on CRTC."); @@ -170,7 +440,7 @@ bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) flip_flags |= DRM_MODE_PAGE_FLIP_ASYNC; } - ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc->crtc_id, + ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, fb_info->fb_id, flip_flags, &windata->waiting_for_flip); if (ret == 0) { @@ -198,6 +468,26 @@ bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) return true; } +bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window * window) +{ + SDL_WindowData *windata = (SDL_WindowData *) window->internal; + + if (windata->swap_window == NULL) { + SDL_VideoData *viddata = _this->internal; + if (viddata->is_atomic) { + // We want the fenced version by default, but it needs extensions. + if ( (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, false)) || (!SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_ANDROID_native_fence_sync")) ) { + windata->swap_window = KMSDRM_GLES_SwapWindowDoubleBuffered; + } else { + windata->swap_window = KMSDRM_GLES_SwapWindowFenced; + } + } else { + windata->swap_window = KMSDRM_GLES_SwapWindowLegacy; + } + } + return windata->swap_window(_this, window); +} + SDL_EGL_MakeCurrent_impl(KMSDRM) #endif // SDL_VIDEO_DRIVER_KMSDRM diff --git a/src/video/kmsdrm/SDL_kmsdrmsym.h b/src/video/kmsdrm/SDL_kmsdrmsym.h index 5f389d854ab2f..4abb1eb48cd81 100644 --- a/src/video/kmsdrm/SDL_kmsdrmsym.h +++ b/src/video/kmsdrm/SDL_kmsdrmsym.h @@ -137,6 +137,16 @@ SDL_KMSDRM_SYM_OPT(uint32_t,gbm_bo_get_offset,(struct gbm_bo *bo, int plane)) SDL_KMSDRM_SYM_OPT(uint32_t,gbm_bo_get_stride_for_plane,(struct gbm_bo *bo, int plane)) SDL_KMSDRM_SYM_OPT(union gbm_bo_handle,gbm_bo_get_handle_for_plane,(struct gbm_bo *bo, int plane)) + +SDL_KMSDRM_MODULE(ATOMIC) +SDL_KMSDRM_SYM(int,drmIoctl,(int fd, unsigned long request, void *arg)) +SDL_KMSDRM_SYM(drmModeAtomicReqPtr,drmModeAtomicAlloc,(void)) +SDL_KMSDRM_SYM(void,drmModeAtomicFree,(drmModeAtomicReqPtr req)) +SDL_KMSDRM_SYM(int,drmModeAtomicCommit,(int fd,drmModeAtomicReqPtr req,uint32_t flags,void *user_data)) +SDL_KMSDRM_SYM(int,drmModeAtomicAddProperty,(drmModeAtomicReqPtr req,uint32_t object_id,uint32_t property_id,uint64_t value)) +SDL_KMSDRM_SYM(int,drmModeCreatePropertyBlob,(int fd,const void *data,size_t size,uint32_t *id)) + + #undef SDL_KMSDRM_MODULE #undef SDL_KMSDRM_SYM #undef SDL_KMSDRM_SYM_CONST diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c index ae9b623583ccd..14ebb343965f1 100644 --- a/src/video/kmsdrm/SDL_kmsdrmvideo.c +++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c @@ -63,6 +63,14 @@ static char kmsdrm_dri_devname[8]; static int kmsdrm_dri_devnamesize = 0; static char kmsdrm_dri_cardpath[32]; +/* for older KMSDRM headers... */ +#ifndef DRM_FORMAT_MOD_VENDOR_NONE +#define DRM_FORMAT_MOD_VENDOR_NONE 0 +#endif +#ifndef DRM_FORMAT_MOD_LINEAR +#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0) +#endif + #ifndef EGL_PLATFORM_GBM_MESA #define EGL_PLATFORM_GBM_MESA 0x31D7 #endif @@ -188,6 +196,409 @@ static void CalculateRefreshRate(drmModeModeInfo *mode, int *numerator, int *den } } + +int add_connector_property(drmModeAtomicReq *req, connector *conn, const char *name, uint64_t value) +{ + unsigned int i; + int prop_id = 0; + + for (i = 0 ; i < conn->props->count_props ; i++) { + if (SDL_strcmp(conn->props_info[i]->name, name) == 0) { + prop_id = conn->props_info[i]->prop_id; + break; + } + } + + if (prop_id < 0) { + SDL_SetError("no connector property: %s", name); + return -EINVAL; + } + + return KMSDRM_drmModeAtomicAddProperty(req, conn->connector->connector_id, prop_id, value); +} + +int add_crtc_property(drmModeAtomicReq *req, crtc *c, const char *name, uint64_t value) +{ + unsigned int i; + int prop_id = -1; + + for (i = 0 ; i < c->props->count_props ; i++) { + if (SDL_strcmp(c->props_info[i]->name, name) == 0) { + prop_id = c->props_info[i]->prop_id; + break; + } + } + + if (prop_id < 0) { + SDL_SetError("no crtc property: %s", name); + return -EINVAL; + } + + return KMSDRM_drmModeAtomicAddProperty(req, c->crtc->crtc_id, prop_id, value); +} + +int add_plane_property(drmModeAtomicReq *req, plane *p, const char *name, uint64_t value) +{ + unsigned int i; + int prop_id = -1; + + for (i = 0 ; i < p->props->count_props ; i++) { + if (SDL_strcmp(p->props_info[i]->name, name) == 0) { + prop_id = p->props_info[i]->prop_id; + break; + } + } + + if (prop_id < 0) { + SDL_SetError("no plane property: %s", name); + return -EINVAL; + } + + return KMSDRM_drmModeAtomicAddProperty(req, p->plane->plane_id, prop_id, value); +} + +#if 0 + +static 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_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_drmModeGetProperty(viddata->drm_fd, props->props[i]); + if ((strcmp(p->name, "type") == 0)) { + type = props->prop_values[i]; + } + + KMSDRM_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_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"); +} + +static 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_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->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_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_drmModeFreePlane(plane); + } + + KMSDRM_drmModeFreePlaneResources(plane_resources); +} + +#endif + +/* Get the plane_id of a plane that is of the specified plane type (primary, + overlay, cursor...) and can use specified CRTC. */ +static int get_plane_id(SDL_VideoDevice *_this, unsigned int crtc_id, uint32_t plane_type) +{ + drmModeRes *resources = NULL; + drmModePlaneResPtr plane_resources = NULL; + uint32_t i, j; + unsigned int crtc_index = 0; + int ret = -EINVAL; + int found = 0; + + SDL_VideoData *viddata = ((SDL_VideoData *)_this->internal); + + resources = KMSDRM_drmModeGetResources(viddata->drm_fd); + + /* Get the crtc_index for the current CRTC. + It's needed to find out if a plane supports the CRTC. */ + for (i = 0; i < resources->count_crtcs; i++) { + if (resources->crtcs[i] == crtc_id) { + crtc_index = i; + break; + } + } + + plane_resources = KMSDRM_drmModeGetPlaneResources(viddata->drm_fd); + if (!plane_resources) { + return SDL_SetError("drmModeGetPlaneResources failed."); + } + + /* Iterate on all the available planes. */ + for (i = 0; (i < plane_resources->count_planes) && !found; i++) { + + uint32_t plane_id = plane_resources->planes[i]; + + drmModePlanePtr _plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id); + if (!_plane) { + continue; + } + + /* See if the current CRTC is available for this plane. */ + if (_plane->possible_crtcs & (1 << crtc_index)) { + + drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties( + viddata->drm_fd, plane_id, DRM_MODE_OBJECT_PLANE); + ret = plane_id; + + /* Iterate on the plane props to find the type of the plane, + to see if it's of the type we want. */ + for (j = 0; j < props->count_props; j++) { + + drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd, + props->props[j]); + + if ((strcmp(p->name, "type") == 0) && (props->prop_values[j] == plane_type)) { + /* found our plane, use that: */ + found = 1; + } + + KMSDRM_drmModeFreeProperty(p); + } + + KMSDRM_drmModeFreeObjectProperties(props); + } + + KMSDRM_drmModeFreePlane(_plane); + } + + KMSDRM_drmModeFreePlaneResources(plane_resources); + KMSDRM_drmModeFreeResources(resources); + + return ret; +} + +/* Setup a plane and it's props. */ +bool setup_plane(SDL_VideoDevice *_this, plane **_plane, uint32_t plane_type) +{ + uint32_t plane_id; + SDL_VideoData *viddata = ((SDL_VideoData *)_this->internal); + SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); + bool ret = true; + + *_plane = SDL_calloc(1, sizeof(**_plane)); + if (!(*_plane)) { + ret = false; + goto cleanup; + } + + /* Get plane ID for a given CRTC and plane type. */ + plane_id = get_plane_id(_this, dispdata->crtc.crtc->crtc_id, plane_type); + + if (!plane_id) { + ret = SDL_SetError("Invalid Plane ID"); + goto cleanup; + } + + /* Get the DRM plane itself. */ + (*_plane)->plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id); + + /* Get the DRM plane properties. */ + if ((*_plane)->plane) { + unsigned int i; + (*_plane)->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd, + (*_plane)->plane->plane_id, DRM_MODE_OBJECT_PLANE); + (*_plane)->props_info = SDL_calloc((*_plane)->props->count_props, sizeof(*(*_plane)->props_info)); + + if ( !((*_plane)->props_info) ) { + ret = false; + goto cleanup; + } + + for (i = 0; i < (*_plane)->props->count_props; i++) { + (*_plane)->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd, (*_plane)->props->props[i]); + } + } + +cleanup: + if (!ret) { + if (*_plane) { + SDL_free(*_plane); + *_plane = NULL; + } + } + return ret; +} + +/* Free a plane and it's props. */ +void free_plane(plane **_plane) +{ + if (*_plane) { + if ((*_plane)->plane) { + KMSDRM_drmModeFreePlane((*_plane)->plane); + (*_plane)->plane = NULL; + } + if ((*_plane)->props_info) { + SDL_free((*_plane)->props_info); + (*_plane)->props_info = NULL; + } + SDL_free(*_plane); + *_plane = NULL; + } +} + +/**********************************************************************************/ +/* The most important ATOMIC fn of the backend. */ +/* A PLANE reads a BUFFER, and a CRTC reads a PLANE and sends it's contents */ +/* over to a CONNECTOR->ENCODER system (several CONNECTORS can be connected */ +/* to the same PLANE). */ +/* Think of a plane as a "frame" sorrounding a picture, where the "picture" */ +/* is the buffer, and we move the "frame" from a picture to another, */ +/* and the one that has the "frame" is the one sent over to the screen */ +/* via the CONNECTOR->ENCODER system. */ +/* Think of a PLANE as being "in the middle", it's the CENTRAL part */ +/* bewteen the CRTC and the BUFFER that is shown on screen. */ +/* What we do here is connect a PLANE to a CRTC and a BUFFER. */ +/* -ALWAYS set the CRTC_ID and FB_ID attribs of a plane at the same time, */ +/* meaning IN THE SAME atomic request. */ +/* -And NEVER destroy a GBM surface whose buffers are being read by a plane: */ +/* first, move the plane away from those buffers and ONLY THEN destroy the */ +/* buffers and/or the GBM surface containig them. */ +/**********************************************************************************/ +void +drm_atomic_set_plane_props(struct KMSDRM_PlaneInfo *info) +{ + SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); + + /* Do we have a set of changes already in the making? If not, allocate a new one. */ + if (!dispdata->atomic_req) { + dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc(); + } + + add_plane_property(dispdata->atomic_req, info->plane, "FB_ID", info->fb_id); + add_plane_property(dispdata->atomic_req, info->plane, "CRTC_ID", info->crtc_id); + add_plane_property(dispdata->atomic_req, info->plane, "SRC_W", info->src_w << 16); + add_plane_property(dispdata->atomic_req, info->plane, "SRC_H", info->src_h << 16); + add_plane_property(dispdata->atomic_req, info->plane, "SRC_X", info->src_x); + add_plane_property(dispdata->atomic_req, info->plane, "SRC_Y", info->src_y); + add_plane_property(dispdata->atomic_req, info->plane, "CRTC_W", info->crtc_w); + add_plane_property(dispdata->atomic_req, info->plane, "CRTC_H", info->crtc_h); + add_plane_property(dispdata->atomic_req, info->plane, "CRTC_X", info->crtc_x); + add_plane_property(dispdata->atomic_req, info->plane, "CRTC_Y", info->crtc_y); +} + +int drm_atomic_commit(SDL_VideoDevice *_this, bool blocking, bool allow_modeset) +{ + SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); + SDL_VideoData *viddata = ((SDL_VideoData *)_this->internal); + uint32_t atomic_flags = 0; + int ret; + + if (!blocking) { + atomic_flags |= DRM_MODE_ATOMIC_NONBLOCK; + } + + if (allow_modeset) { + atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + } + + /* Never issue a new atomic commit if previous has not yet completed, + or it will error. */ + drm_atomic_waitpending(_this); + + ret = KMSDRM_drmModeAtomicCommit(viddata->drm_fd, dispdata->atomic_req, + atomic_flags, NULL); + + if (ret) { + SDL_SetError("Atomic commit failed, returned %d.", ret); + /* Uncomment this for fast-debugging */ +#if 0 + printf("ATOMIC COMMIT FAILED: %s.\n", strerror(errno)); +#endif + goto out; + } + + if (dispdata->kms_in_fence_fd != -1) { + close(dispdata->kms_in_fence_fd); + dispdata->kms_in_fence_fd = -1; + } + +out: + KMSDRM_drmModeAtomicFree(dispdata->atomic_req); + dispdata->atomic_req = NULL; + + return ret; +} + +void +drm_atomic_waitpending(SDL_VideoDevice *_this) +{ + SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); + + /* Will return immediately if we have already destroyed the fence, because we NULL-ify it just after. + Also, will return immediately in double-buffer mode, because kms_fence will alsawys be NULL. */ + if (dispdata->kms_fence) { + EGLint status; + + do { + status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display, + dispdata->kms_fence, 0, EGL_FOREVER_KHR); + } while (status != EGL_CONDITION_SATISFIED_KHR); + + _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->kms_fence); + dispdata->kms_fence = NULL; + } +} + static bool KMSDRM_Available(void) { #ifdef SDL_PLATFORM_OPENBSD @@ -515,14 +926,14 @@ static drmModeModeInfo *KMSDRM_GetClosestDisplayMode(SDL_VideoDisplay *display, { SDL_DisplayData *dispdata = display->internal; - drmModeConnector *connector = dispdata->connector; + drmModeConnector *conn = dispdata->connector.connector; SDL_DisplayMode closest; drmModeModeInfo *drm_mode; if (SDL_GetClosestFullscreenDisplayMode(display->id, width, height, 0.0f, false, &closest)) { const SDL_DisplayModeData *modedata = closest.internal; - drm_mode = &connector->modes[modedata->mode_index]; + drm_mode = &conn->modes[modedata->mode_index]; return drm_mode; } else { return NULL; @@ -538,6 +949,11 @@ static bool KMSDRM_DropMaster(SDL_VideoDevice *_this) { SDL_VideoData *viddata = _this->internal; + if (viddata->is_atomic) { // turn off atomic support until we are in control again. + KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 0); + KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0); + } + /* Check if we have DRM master to begin with */ if (KMSDRM_drmAuthMagic(viddata->drm_fd, 0) == -EACCES) { /* Nope, nothing to do then */ @@ -564,15 +980,15 @@ static void KMSDRM_DeinitDisplays(SDL_VideoDevice *_this) dispdata = SDL_GetDisplayDriverData(displays[i]); // Free connector - if (dispdata && dispdata->connector) { - KMSDRM_drmModeFreeConnector(dispdata->connector); - dispdata->connector = NULL; + if (dispdata && dispdata->connector.connector) { + KMSDRM_drmModeFreeConnector(dispdata->connector.connector); + dispdata->connector.connector = NULL; } // Free CRTC - if (dispdata && dispdata->crtc) { - KMSDRM_drmModeFreeCrtc(dispdata->crtc); - dispdata->crtc = NULL; + if (dispdata && dispdata->crtc.crtc) { + KMSDRM_drmModeFreeCrtc(dispdata->crtc.crtc); + dispdata->crtc.crtc = NULL; } } SDL_free(displays); @@ -791,14 +1207,14 @@ static int KMSDRM_CrtcGetOrientation(uint32_t drm_fd, uint32_t crtc_id) /* Gets a DRM connector, builds an SDL_Display with it, and adds it to the list of SDL Displays in _this->displays[] */ -static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connector, drmModeRes *resources) +static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *conn, drmModeRes *resources) { SDL_VideoData *viddata = _this->internal; SDL_DisplayData *dispdata = NULL; SDL_VideoDisplay display = { 0 }; SDL_DisplayModeData *modedata = NULL; drmModeEncoder *encoder = NULL; - drmModeCrtc *crtc = NULL; + drmModeCrtc *_crtc = NULL; const char *connector_type = NULL; SDL_DisplayID display_id; SDL_PropertiesID display_properties; @@ -819,6 +1235,7 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto to sane values. */ dispdata->cursor_bo = NULL; dispdata->cursor_bo_drm_fd = -1; + dispdata->kms_out_fence_fd = -1; /* Since we create and show the default cursor on KMSDRM_InitMouse(), and we call KMSDRM_InitMouse() when we create a window, we have to know @@ -835,7 +1252,7 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto continue; } - if (encoder->encoder_id == connector->encoder_id) { + if (encoder->encoder_id == conn->encoder_id) { break; } @@ -853,13 +1270,13 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto continue; } - for (j = 0; j < connector->count_encoders; j++) { - if (connector->encoders[j] == encoder->encoder_id) { + for (j = 0; j < conn->count_encoders; j++) { + if (conn->encoders[j] == encoder->encoder_id) { break; } } - if (j != connector->count_encoders) { + if (j != conn->count_encoders) { break; } @@ -874,21 +1291,21 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto } // Try to find a CRTC connected to this encoder - crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id); + _crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id); /* If no CRTC was connected to the encoder, find the first CRTC that is supported by the encoder, and use that. */ - if (!crtc) { + if (!_crtc) { for (i = 0; i < resources->count_crtcs; i++) { if (encoder->possible_crtcs & (1 << i)) { encoder->crtc_id = resources->crtcs[i]; - crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id); + _crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id); break; } } } - if (!crtc) { + if (!_crtc) { ret = SDL_SetError("No CRTC found for connector."); goto cleanup; } @@ -896,10 +1313,10 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto // Find the index of the mode attached to this CRTC mode_index = -1; - for (i = 0; i < connector->count_modes; i++) { - drmModeModeInfo *mode = &connector->modes[i]; + for (i = 0; i < conn->count_modes; i++) { + drmModeModeInfo *mode = &conn->modes[i]; - if (!SDL_memcmp(mode, &crtc->mode, sizeof(crtc->mode))) { + if (!SDL_memcmp(mode, &_crtc->mode, sizeof(_crtc->mode))) { mode_index = i; break; } @@ -909,8 +1326,8 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto int current_area, largest_area = 0; // Find the preferred mode or the highest resolution mode - for (i = 0; i < connector->count_modes; i++) { - drmModeModeInfo *mode = &connector->modes[i]; + for (i = 0; i < conn->count_modes; i++) { + drmModeModeInfo *mode = &conn->modes[i]; if (mode->type & DRM_MODE_TYPE_PREFERRED) { mode_index = i; @@ -924,7 +1341,7 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto } } if (mode_index != -1) { - crtc->mode = connector->modes[mode_index]; + _crtc->mode = conn->modes[mode_index]; } } @@ -944,9 +1361,9 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto /* Get the mode currently setup for this display, which is the mode currently setup on the CRTC we found for the active connector. */ - dispdata->mode = crtc->mode; - dispdata->original_mode = crtc->mode; - dispdata->fullscreen_mode = crtc->mode; + dispdata->mode = _crtc->mode; + dispdata->original_mode = _crtc->mode; + dispdata->fullscreen_mode = _crtc->mode; if (dispdata->mode.hdisplay == 0 || dispdata->mode.vdisplay == 0) { ret = SDL_SetError("Couldn't get a valid connector videomode."); @@ -954,24 +1371,48 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto } // Store the connector and crtc for this display. - dispdata->connector = connector; - dispdata->crtc = crtc; + dispdata->connector.connector = conn; + dispdata->crtc.crtc = _crtc; // save previous vrr state - dispdata->saved_vrr = KMSDRM_CrtcGetVrr(viddata->drm_fd, crtc->crtc_id); + dispdata->saved_vrr = KMSDRM_CrtcGetVrr(viddata->drm_fd, _crtc->crtc_id); // try to enable vrr - if (KMSDRM_ConnectorCheckVrrCapable(viddata->drm_fd, connector->connector_id, "VRR_CAPABLE")) { + if (KMSDRM_ConnectorCheckVrrCapable(viddata->drm_fd, conn->connector_id, "VRR_CAPABLE")) { SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Enabling VRR"); - KMSDRM_CrtcSetVrr(viddata->drm_fd, crtc->crtc_id, true); + KMSDRM_CrtcSetVrr(viddata->drm_fd, _crtc->crtc_id, true); } // Set the name by the connector type, if possible if (KMSDRM_drmModeGetConnectorTypeName) { - connector_type = KMSDRM_drmModeGetConnectorTypeName(connector->connector_type); + connector_type = KMSDRM_drmModeGetConnectorTypeName(conn->connector_type); if (connector_type == NULL) { connector_type = "Unknown"; } - SDL_snprintf(name_fmt, sizeof(name_fmt), "%s-%u", connector_type, connector->connector_type_id); + SDL_snprintf(name_fmt, sizeof(name_fmt), "%s-%u", connector_type, conn->connector_type_id); + } + + dispdata->crtc.props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd, _crtc->crtc_id, DRM_MODE_OBJECT_CRTC); + dispdata->crtc.props_info = SDL_calloc(dispdata->crtc.props->count_props, sizeof(*dispdata->crtc.props_info)); + if (!dispdata->crtc.props_info) { + ret = false; + goto cleanup; + } + + for (i = 0; i < dispdata->crtc.props->count_props; i++) { + dispdata->crtc.props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd, dispdata->crtc.props->props[i]); + } + + /* Get connector properties */ + dispdata->connector.props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd, conn->connector_id, DRM_MODE_OBJECT_CONNECTOR); + dispdata->connector.props_info = SDL_calloc(dispdata->connector.props->count_props, sizeof(*dispdata->connector.props_info)); + if (!dispdata->connector.props_info) { + ret = false; + goto cleanup; + } + + for (i = 0; i < dispdata->connector.props->count_props; i++) { + dispdata->connector.props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd, + dispdata->connector.props->props[i]); } /*****************************************/ @@ -1006,7 +1447,7 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto goto cleanup; } - orientation = KMSDRM_CrtcGetOrientation(viddata->drm_fd, crtc->crtc_id); + orientation = KMSDRM_CrtcGetOrientation(viddata->drm_fd, _crtc->crtc_id); display_properties = SDL_GetDisplayProperties(display_id); SDL_SetNumberProperty(display_properties, SDL_PROP_DISPLAY_KMSDRM_PANEL_ORIENTATION_NUMBER, orientation); @@ -1017,14 +1458,15 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto if (ret) { // Error (complete) cleanup if (dispdata) { - if (dispdata->connector) { - KMSDRM_drmModeFreeConnector(dispdata->connector); - dispdata->connector = NULL; + if (dispdata->connector.connector) { + KMSDRM_drmModeFreeConnector(dispdata->connector.connector); } - if (dispdata->crtc) { - KMSDRM_drmModeFreeCrtc(dispdata->crtc); - dispdata->crtc = NULL; + if (dispdata->crtc.crtc) { + KMSDRM_drmModeFreeCrtc(dispdata->crtc.crtc); } + SDL_free(dispdata->connector.props_info); + SDL_free(dispdata->crtc.props_info); + SDL_free(dispdata->display_plane); SDL_free(dispdata); } } @@ -1073,13 +1515,22 @@ static void KMSDRM_SortDisplays(SDL_VideoDevice *_this) } } +static bool set_client_atomic_caps(int fd) +{ + if (KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1)) { + return false; // no atomic modesetting support. + } else if (KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) { + return false; // no universal planes support. + } + return true; +} + /* Initializes the list of SDL displays: we build a new display for each connecter connector we find. This is to be called early, in VideoInit(), because it gets us the videomode information, which SDL needs immediately after VideoInit(). */ static bool KMSDRM_InitDisplays(SDL_VideoDevice *_this) { - SDL_VideoData *viddata = _this->internal; drmModeRes *resources = NULL; uint64_t async_pageflip = 0; @@ -1100,6 +1551,11 @@ static bool KMSDRM_InitDisplays(SDL_VideoDevice *_this) SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd); + // Set ATOMIC & UNIVERSAL PLANES compatibility + viddata->is_atomic = set_client_atomic_caps(viddata->drm_fd); + + SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "DRM FD (%d) %s atomic", viddata->drm_fd, viddata->is_atomic ? "SUPPORTS" : "DOES NOT SUPPORT"); + // Get all of the available connectors / devices / crtcs resources = KMSDRM_drmModeGetResources(viddata->drm_fd); if (!resources) { @@ -1110,22 +1566,20 @@ static bool KMSDRM_InitDisplays(SDL_VideoDevice *_this) /* Iterate on the available connectors. For every connected connector, we create an SDL_Display and add it to the list of SDL Displays. */ for (i = 0; i < resources->count_connectors; i++) { - drmModeConnector *connector = KMSDRM_drmModeGetConnector(viddata->drm_fd, - resources->connectors[i]); - - if (!connector) { + drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd, resources->connectors[i]); + if (!conn) { continue; } - if (connector->connection == DRM_MODE_CONNECTED && connector->count_modes) { + if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) { /* If it's a connected connector with available videomodes, try to add an SDL Display representing it. KMSDRM_AddDisplay() is purposely void, so if it fails (no encoder for connector, no valid video mode for connector etc...) we can keep looking for connected connectors. */ - KMSDRM_AddDisplay(_this, connector, resources); + KMSDRM_AddDisplay(_this, conn, resources); } else { // If it's not, free it now. - KMSDRM_drmModeFreeConnector(connector); + KMSDRM_drmModeFreeConnector(conn); } } @@ -1190,6 +1644,8 @@ static bool KMSDRM_GBMInit(SDL_VideoDevice *_this, SDL_DisplayData *dispdata) } } + viddata->is_atomic = set_client_atomic_caps(viddata->drm_fd); + // Set the FD as current DRM master. KMSDRM_drmSetMaster(viddata->drm_fd); @@ -1197,6 +1653,13 @@ static bool KMSDRM_GBMInit(SDL_VideoDevice *_this, SDL_DisplayData *dispdata) viddata->gbm_dev = KMSDRM_gbm_create_device(viddata->drm_fd); if (!viddata->gbm_dev) { result = SDL_SetError("Couldn't create gbm device."); + } else { + // Setup the display plane. ONLY do this after dispdata has the right + // crtc and connector, because these are used in this function. + result = setup_plane(_this, &dispdata->display_plane, DRM_PLANE_TYPE_PRIMARY); + if (!result) { + SDL_SetError("can't find suitable display plane."); + } } viddata->gbm_init = true; @@ -1209,6 +1672,12 @@ static void KMSDRM_GBMDeinit(SDL_VideoDevice *_this, SDL_DisplayData *dispdata) { SDL_VideoData *viddata = _this->internal; + // Free display plane + free_plane(&dispdata->display_plane); + + // Free cursor plane (if still not freed) + free_plane(&dispdata->cursor_plane); + /* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(), already called when we get here. */ if (viddata->gbm_dev) { @@ -1238,19 +1707,86 @@ static void KMSDRM_DestroySurfaces(SDL_VideoDevice *_this, SDL_Window *window) /**********************************************/ // KMSDRM_WaitPageflip(_this, windata); + if (viddata->is_atomic) { + + /* TODO : Continue investigating why this doesn't work. We should do this instead + of making the display plane point to the TTY console, which isn't there + after creating and destroying a Vulkan window. */ + +#if 0 // (note that this code has bitrotted a little, in addition to TODO comment above.) + /* Disconnect the connector from the CRTC (remember: several connectors + can read a CRTC), deactivate the CRTC, and set the PRIMARY PLANE props + CRTC_ID and FB_ID to 0. Then we can destroy the GBM buffers and surface. */ + add_connector_property(dispdata->atomic_req, dispdata->connector , "CRTC_ID", 0); + add_crtc_property(dispdata->atomic_req, dispdata->crtc , "MODE_ID", 0); + add_crtc_property(dispdata->atomic_req, dispdata->crtc , "active", 0); + /**********************************************/ + /* Wait for last issued pageflip to complete. */ + /**********************************************/ + KMSDRM_WaitPageFlip(_this, windata, -1); + + plane_info.plane = dispdata->display_plane; + plane_info.crtc_id = 0; + plane_info.fb_id = 0; + /***********************************************************************/ + /* Restore the original CRTC configuration: configue the crtc with the */ + /* original video mode and make it point to the original TTY buffer. */ + /***********************************************************************/ + + drm_atomic_set_plane_props(&plane_info); + ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id, + dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1, + &dispdata->original_mode); + + /* Issue atomic commit that is blocking and allows modesetting. */ + if (drm_atomic_commit(_this, true, true)) { + SDL_SetError("Failed to issue atomic commit on surfaces destruction."); + /* If we failed to set the original mode, try to set the connector prefered mode. */ + if (ret && (dispdata->crtc->mode_valid == 0)) { + ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id, + dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1, + &dispdata->original_mode); + } +#endif + +#if 1 + /************************************************************/ + /* Make the display plane point to the original TTY buffer. */ + /* We have to configure it's input and output scaling */ + /* parameters accordingly. */ + /************************************************************/ + + KMSDRM_PlaneInfo plane_info; + SDL_zero(plane_info); + plane_info.plane = dispdata->display_plane; + plane_info.crtc_id = dispdata->crtc.crtc->crtc_id; + plane_info.fb_id = dispdata->crtc.crtc->buffer_id; + plane_info.src_w = dispdata->original_mode.hdisplay; + plane_info.src_h = dispdata->original_mode.vdisplay; + plane_info.crtc_w = dispdata->original_mode.hdisplay; + plane_info.crtc_h = dispdata->original_mode.vdisplay; + + drm_atomic_set_plane_props(&plane_info); + + if (drm_atomic_commit(_this, true, false)) { + SDL_SetError("Failed to issue atomic commit on surfaces destruction."); + } + } +#endif + /************************************************************************/ // Restore the original CRTC configuration: configure the crtc with the // original video mode and make it point to the original TTY buffer. /************************************************************************/ - ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id, - dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1, + ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, + dispdata->crtc.crtc->buffer_id, 0, 0, &dispdata->connector.connector->connector_id, 1, &dispdata->original_mode); // If we failed to set the original mode, try to set the connector preferred mode. - if (ret && (dispdata->crtc->mode_valid == 0)) { - ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id, - dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1, + if (ret && (dispdata->crtc.crtc->mode_valid == 0)) { + ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, + dispdata->crtc.crtc->buffer_id, 0, 0, &dispdata->connector.connector->connector_id, 1, &dispdata->original_mode); } @@ -1453,6 +1989,10 @@ bool KMSDRM_VideoInit(SDL_VideoDevice *_this) result = SDL_SetError("error getting KMSDRM displays information"); } +#if 0 // Use this if you ever need to see info on all available planes. + get_planes_info(_this); +#endif + #ifdef SDL_INPUT_LINUXEV SDL_EVDEV_Init(); SDL_EVDEV_SetVTSwitchCallbacks(KMSDRM_ReleaseVT, _this, KMSDRM_AcquireVT, _this); @@ -1492,7 +2032,7 @@ void KMSDRM_VideoQuit(SDL_VideoDevice *_this) bool KMSDRM_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display) { SDL_DisplayData *dispdata = display->internal; - drmModeConnector *conn = dispdata->connector; + drmModeConnector *conn = dispdata->connector.connector; SDL_DisplayMode mode; int i; @@ -1525,7 +2065,7 @@ bool KMSDRM_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SD SDL_VideoData *viddata = _this->internal; SDL_DisplayData *dispdata = display->internal; SDL_DisplayModeData *modedata = mode->internal; - drmModeConnector *conn = dispdata->connector; + drmModeConnector *conn = dispdata->connector.connector; int i; // Don't do anything if we are in Vulkan mode. @@ -1561,7 +2101,7 @@ void KMSDRM_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) } // restore vrr state - KMSDRM_CrtcSetVrr(windata->viddata->drm_fd, dispdata->crtc->crtc_id, dispdata->saved_vrr); + KMSDRM_CrtcSetVrr(windata->viddata->drm_fd, dispdata->crtc.crtc->crtc_id, dispdata->saved_vrr); viddata = windata->viddata; @@ -1623,7 +2163,7 @@ void KMSDRM_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) /**********************************************************************/ // We simply IGNORE if it's a fullscreen window, window->flags don't -// reflect it: if it's fullscreen, KMSDRM_SetWindwoFullscreen() will +// reflect it: if it's fullscreen, KMSDRM_SetWindowFullscreen() will // be called by SDL later, and we can manage it there. /**********************************************************************/ bool KMSDRM_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props) diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.h b/src/video/kmsdrm/SDL_kmsdrmvideo.h index 16571ecff2926..867acb99174ef 100644 --- a/src/video/kmsdrm/SDL_kmsdrmvideo.h +++ b/src/video/kmsdrm/SDL_kmsdrmvideo.h @@ -19,6 +19,8 @@ 3. This notice may not be removed or altered from any source distribution. */ +// Atomic KMSDRM backend originally written by Manuel Alfayate Corchete + #include "SDL_internal.h" #ifndef SDL_kmsdrmvideo_h @@ -32,6 +34,7 @@ #include #include #include +#include #ifndef DRM_FORMAT_MOD_INVALID #define DRM_FORMAT_MOD_INVALID 0x00ffffffffffffffULL @@ -72,6 +75,24 @@ #define GBM_BO_USE_LINEAR (1 << 4) #endif +typedef struct plane { + drmModePlane *plane; + drmModeObjectProperties *props; + drmModePropertyRes **props_info; +} plane; + +typedef struct crtc { + drmModeCrtc *crtc; + drmModeObjectProperties *props; + drmModePropertyRes **props_info; +} crtc; + +typedef struct connector { + drmModeConnector *connector; + drmModeObjectProperties *props; + drmModePropertyRes **props_info; +} connector; + struct SDL_VideoData { int devindex; // device index that was passed on creation @@ -92,6 +113,7 @@ struct SDL_VideoData open 1 FD and create 1 gbm device. */ bool gbm_init; + bool is_atomic; // true if atomic interfaces are supported. }; struct SDL_DisplayModeData @@ -101,8 +123,11 @@ struct SDL_DisplayModeData struct SDL_DisplayData { - drmModeConnector *connector; - drmModeCrtc *crtc; + plane *display_plane; + plane *cursor_plane; + crtc crtc; + connector connector; + drmModeModeInfo mode; drmModeModeInfo original_mode; drmModeModeInfo fullscreen_mode; @@ -118,6 +143,15 @@ struct SDL_DisplayData int cursor_bo_drm_fd; uint64_t cursor_w, cursor_h; + /* Central atomic request list, used for the prop + changeset related to pageflip in SwapWindow. */ + drmModeAtomicReq *atomic_req; + + int kms_in_fence_fd; + int kms_out_fence_fd; + EGLSyncKHR kms_fence; + EGLSyncKHR gpu_fence; + bool default_cursor_init; }; @@ -137,6 +171,9 @@ struct SDL_WindowData EGLSurface egl_surface; bool egl_surface_dirty; + + /* This dictates what approach we'll use for SwapBuffers. */ + bool (*swap_window)(SDL_VideoDevice *_this, SDL_Window *window); }; typedef struct KMSDRM_FBInfo @@ -145,12 +182,37 @@ typedef struct KMSDRM_FBInfo uint32_t fb_id; // DRM framebuffer ID } KMSDRM_FBInfo; +typedef struct KMSDRM_PlaneInfo +{ + struct plane *plane; + uint32_t fb_id; + uint32_t crtc_id; + int32_t src_x; + int32_t src_y; + int32_t src_w; + int32_t src_h; + int32_t crtc_x; + int32_t crtc_y; + int32_t crtc_w; + int32_t crtc_h; +} KMSDRM_PlaneInfo; + // Helper functions extern bool KMSDRM_CreateSurfaces(SDL_VideoDevice *_this, SDL_Window *window); extern KMSDRM_FBInfo *KMSDRM_FBFromBO(SDL_VideoDevice *_this, struct gbm_bo *bo); extern KMSDRM_FBInfo *KMSDRM_FBFromBO2(SDL_VideoDevice *_this, struct gbm_bo *bo, int w, int h); extern bool KMSDRM_WaitPageflip(SDL_VideoDevice *_this, SDL_WindowData *windata); +// Atomic functions that are used from SDL_kmsdrmopengles.c and SDL_kmsdrmmouse.c +void drm_atomic_set_plane_props(struct KMSDRM_PlaneInfo *info); +void drm_atomic_waitpending(SDL_VideoDevice *_this); +int drm_atomic_commit(SDL_VideoDevice *_this, bool blocking, bool allow_modeset); +int add_plane_property(drmModeAtomicReq *req, struct plane *plane, const char *name, uint64_t value); +int add_crtc_property(drmModeAtomicReq *req, struct crtc *crtc, const char *name, uint64_t value); +int add_connector_property(drmModeAtomicReq *req, struct connector *connector, const char *name, uint64_t value); +bool setup_plane(SDL_VideoDevice *_this, struct plane **plane, uint32_t plane_type); +void free_plane(struct plane **plane); + /****************************************************************************/ // SDL_VideoDevice functions declaration /****************************************************************************/ diff --git a/src/video/kmsdrm/SDL_kmsdrmvulkan.c b/src/video/kmsdrm/SDL_kmsdrmvulkan.c index ef6b39edec34c..390f38a136842 100644 --- a/src/video/kmsdrm/SDL_kmsdrmvulkan.c +++ b/src/video/kmsdrm/SDL_kmsdrmvulkan.c @@ -171,7 +171,7 @@ bool KMSDRM_Vulkan_CreateSurface(SDL_VideoDevice *_this, uint32_t display_count; uint32_t mode_count; uint32_t plane_count; - uint32_t plane = UINT32_MAX; + uint32_t _plane = UINT32_MAX; VkPhysicalDevice *physical_devices = NULL; VkPhysicalDeviceProperties *device_props = NULL; @@ -450,13 +450,13 @@ bool KMSDRM_Vulkan_CreateSurface(SDL_VideoDevice *_this, vkGetDisplayPlaneCapabilitiesKHR(gpu, display_mode, i, &plane_caps); if (plane_caps.supportedAlpha == alpha_mode) { // Yep, this plane is alright. - plane = i; + _plane = i; break; } } // If we couldn't find an appropriate plane, error out. - if (plane == UINT32_MAX) { + if (_plane == UINT32_MAX) { SDL_SetError("Vulkan couldn't find an appropriate plane."); goto clean; } @@ -471,7 +471,7 @@ bool KMSDRM_Vulkan_CreateSurface(SDL_VideoDevice *_this, SDL_zero(display_plane_surface_create_info); display_plane_surface_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR; display_plane_surface_create_info.displayMode = display_mode; - display_plane_surface_create_info.planeIndex = plane; + display_plane_surface_create_info.planeIndex = _plane; display_plane_surface_create_info.imageExtent = image_size; display_plane_surface_create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; display_plane_surface_create_info.alphaMode = alpha_mode;