2017-08-02 19:22:48 +02:00
|
|
|
/*
|
|
|
|
Simple DirectMedia Layer
|
2020-01-17 05:49:25 +01:00
|
|
|
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
2017-08-02 19:22:48 +02:00
|
|
|
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
|
|
warranty. In no event will the authors be held liable for any damages
|
|
|
|
arising from the use of this software.
|
|
|
|
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
|
|
including commercial applications, and to alter it and redistribute it
|
|
|
|
freely, subject to the following restrictions:
|
|
|
|
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
|
|
claim that you wrote the original software. If you use this software
|
|
|
|
in a product, an acknowledgment in the product documentation would be
|
|
|
|
appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
|
|
misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "../../SDL_internal.h"
|
|
|
|
|
|
|
|
#if SDL_VIDEO_DRIVER_KMSDRM
|
|
|
|
|
|
|
|
/* SDL internals */
|
|
|
|
#include "../SDL_sysvideo.h"
|
|
|
|
#include "SDL_syswm.h"
|
2017-10-27 01:37:20 +02:00
|
|
|
#include "SDL_hints.h"
|
2020-02-09 20:44:22 +01:00
|
|
|
#include "../../events/SDL_events_c.h"
|
2017-08-02 19:22:48 +02:00
|
|
|
#include "../../events/SDL_mouse_c.h"
|
|
|
|
#include "../../events/SDL_keyboard_c.h"
|
|
|
|
|
|
|
|
#ifdef SDL_INPUT_LINUXEV
|
|
|
|
#include "../../core/linux/SDL_evdev.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* KMS/DRM declarations */
|
|
|
|
#include "SDL_kmsdrmvideo.h"
|
2017-08-22 02:20:50 +02:00
|
|
|
#include "SDL_kmsdrmevents.h"
|
2017-08-02 19:22:48 +02:00
|
|
|
#include "SDL_kmsdrmopengles.h"
|
|
|
|
#include "SDL_kmsdrmmouse.h"
|
|
|
|
#include "SDL_kmsdrmdyn.h"
|
2018-10-09 06:27:55 +02:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <errno.h>
|
2020-02-09 20:44:22 +01:00
|
|
|
#include <poll.h>
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2018-10-09 06:27:55 +02:00
|
|
|
#define KMSDRM_DRI_PATH "/dev/dri/"
|
2017-08-02 19:22:48 +02:00
|
|
|
|
|
|
|
static int
|
2018-10-09 06:27:55 +02:00
|
|
|
check_modestting(int devindex)
|
2017-08-02 19:22:48 +02:00
|
|
|
{
|
2018-10-09 06:27:55 +02:00
|
|
|
SDL_bool available = SDL_FALSE;
|
|
|
|
char device[512];
|
|
|
|
int drm_fd;
|
|
|
|
|
|
|
|
SDL_snprintf(device, sizeof (device), "%scard%d", KMSDRM_DRI_PATH, devindex);
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2018-10-09 06:27:55 +02:00
|
|
|
drm_fd = open(device, O_RDWR | O_CLOEXEC);
|
2017-08-02 19:22:48 +02:00
|
|
|
if (drm_fd >= 0) {
|
|
|
|
if (SDL_KMSDRM_LoadSymbols()) {
|
|
|
|
drmModeRes *resources = KMSDRM_drmModeGetResources(drm_fd);
|
2020-02-09 20:44:22 +01:00
|
|
|
if (resources) {
|
2018-12-01 22:31:56 +01:00
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "%scard%d connector, encoder and CRTC counts are: %d %d %d",
|
|
|
|
KMSDRM_DRI_PATH, devindex,
|
|
|
|
resources->count_connectors, resources->count_encoders, resources->count_crtcs);
|
|
|
|
|
|
|
|
if (resources->count_connectors > 0 && resources->count_encoders > 0 && resources->count_crtcs > 0) {
|
|
|
|
available = SDL_TRUE;
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
KMSDRM_drmModeFreeResources(resources);
|
|
|
|
}
|
|
|
|
SDL_KMSDRM_UnloadSymbols();
|
|
|
|
}
|
|
|
|
close(drm_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return available;
|
|
|
|
}
|
|
|
|
|
2018-10-09 06:27:55 +02:00
|
|
|
static int get_dricount(void)
|
|
|
|
{
|
|
|
|
int devcount = 0;
|
|
|
|
struct dirent *res;
|
|
|
|
struct stat sb;
|
|
|
|
DIR *folder;
|
|
|
|
|
|
|
|
if (!(stat(KMSDRM_DRI_PATH, &sb) == 0
|
|
|
|
&& S_ISDIR(sb.st_mode))) {
|
|
|
|
printf("The path %s cannot be opened or is not available\n",
|
|
|
|
KMSDRM_DRI_PATH);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (access(KMSDRM_DRI_PATH, F_OK) == -1) {
|
|
|
|
printf("The path %s cannot be opened\n",
|
|
|
|
KMSDRM_DRI_PATH);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
folder = opendir(KMSDRM_DRI_PATH);
|
|
|
|
if (folder) {
|
|
|
|
while ((res = readdir(folder))) {
|
2019-06-19 10:11:38 +02:00
|
|
|
int len = SDL_strlen(res->d_name);
|
2019-06-19 15:40:50 +02:00
|
|
|
if (len > 4 && SDL_strncmp(res->d_name, "card", 4) == 0) {
|
2018-10-09 06:27:55 +02:00
|
|
|
devcount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(folder);
|
|
|
|
}
|
|
|
|
|
|
|
|
return devcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_driindex(void)
|
|
|
|
{
|
|
|
|
const int devcount = get_dricount();
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < devcount; i++) {
|
|
|
|
if (check_modestting(i)) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
KMSDRM_Available(void)
|
|
|
|
{
|
|
|
|
int ret = -ENOENT;
|
|
|
|
|
|
|
|
ret = get_driindex();
|
|
|
|
if (ret >= 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
static void
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_DeleteDevice(SDL_VideoDevice * device)
|
2017-08-02 19:22:48 +02:00
|
|
|
{
|
2020-02-09 20:44:22 +01:00
|
|
|
if (device->driverdata) {
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_free(device->driverdata);
|
|
|
|
device->driverdata = NULL;
|
|
|
|
}
|
2017-08-02 22:51:14 +02:00
|
|
|
|
|
|
|
SDL_free(device);
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_KMSDRM_UnloadSymbols();
|
|
|
|
}
|
|
|
|
|
|
|
|
static SDL_VideoDevice *
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_CreateDevice(int devindex)
|
2017-08-02 19:22:48 +02:00
|
|
|
{
|
|
|
|
SDL_VideoDevice *device;
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_VideoData *viddata;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2018-10-09 06:27:55 +02:00
|
|
|
if (!devindex || (devindex > 99)) {
|
|
|
|
devindex = get_driindex();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (devindex < 0) {
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_SetError("devindex (%d) must be between 0 and 99.\n", devindex);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SDL_KMSDRM_LoadSymbols()) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
|
2020-02-09 20:44:22 +01:00
|
|
|
if (!device) {
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_OutOfMemory();
|
2017-08-02 19:24:47 +02:00
|
|
|
return NULL;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
viddata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
|
|
|
|
if (!viddata) {
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_OutOfMemory();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
viddata->devindex = devindex;
|
|
|
|
viddata->drm_fd = -1;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
device->driverdata = viddata;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
Fixed bug 5140 - KMSDRM: Dynamic vsync toggle does not work
Manuel Alfayate Corchete
The KMSDRM backend was doing things wrong because of some small (but important) misconceptions on how KMS/DRM works: to implement a largely broken non-vsync refresh mechanism, the SwapWindow() function was issuing new pageflips before previous ones had completed, thus causing EBUSY returns, buffer mismanagement, etc... resulting in general breakage on vsync disabling from apps, that would not allow vsync to work again without KMSDRM video re-initialization.
To further clarify, on most DRM drivers async pageflips are NOT working nowadays, so all issued pageflips will complete on next VBLANK, NOT ASAP (calling drmModePageFlip() with the DRM_MODE_PAGE_FLIP_ASYNC flag will return error).
The old code was assuming that can just issue a synchronous (=on VBLANK) pageflip and then pass a 0 timeout to the pull() function so we do not wait for the pageflip event, thinking that this will lead to correct non-vsynced screen updates from the program: That is plain wrong.
Each pageflip has to be waite before issuing a new one, ALWAYS. And if we do not support ASYNC pageflips on the DRM driver level, then we are forced to wait for the next VBLANK. There is no way around it.
I have also added many comments on the KMSDRM code. This is needed for future reference for me or others who may need to look at this code: KMS/DRM terminology regarding what SYNC and ASYNC mean in pageflip terms, and where to do certain things and why, is not trivial. It is not desirable or possible to invest time on researching the same concepts every time there is need to dive into this code. So please leave all these comments in the patch.
2020-05-27 01:27:00 +02:00
|
|
|
/* Setup all functions that can be handled from this backend. */
|
2017-08-02 19:22:48 +02:00
|
|
|
device->VideoInit = KMSDRM_VideoInit;
|
|
|
|
device->VideoQuit = KMSDRM_VideoQuit;
|
|
|
|
device->GetDisplayModes = KMSDRM_GetDisplayModes;
|
|
|
|
device->SetDisplayMode = KMSDRM_SetDisplayMode;
|
2017-08-28 09:43:14 +02:00
|
|
|
device->CreateSDLWindow = KMSDRM_CreateWindow;
|
|
|
|
device->CreateSDLWindowFrom = KMSDRM_CreateWindowFrom;
|
2017-08-02 19:22:48 +02:00
|
|
|
device->SetWindowTitle = KMSDRM_SetWindowTitle;
|
|
|
|
device->SetWindowIcon = KMSDRM_SetWindowIcon;
|
|
|
|
device->SetWindowPosition = KMSDRM_SetWindowPosition;
|
|
|
|
device->SetWindowSize = KMSDRM_SetWindowSize;
|
|
|
|
device->ShowWindow = KMSDRM_ShowWindow;
|
|
|
|
device->HideWindow = KMSDRM_HideWindow;
|
|
|
|
device->RaiseWindow = KMSDRM_RaiseWindow;
|
|
|
|
device->MaximizeWindow = KMSDRM_MaximizeWindow;
|
|
|
|
device->MinimizeWindow = KMSDRM_MinimizeWindow;
|
|
|
|
device->RestoreWindow = KMSDRM_RestoreWindow;
|
|
|
|
device->SetWindowGrab = KMSDRM_SetWindowGrab;
|
|
|
|
device->DestroyWindow = KMSDRM_DestroyWindow;
|
|
|
|
device->GetWindowWMInfo = KMSDRM_GetWindowWMInfo;
|
2017-08-04 23:00:47 +02:00
|
|
|
#if SDL_VIDEO_OPENGL_EGL
|
2017-08-02 19:22:48 +02:00
|
|
|
device->GL_LoadLibrary = KMSDRM_GLES_LoadLibrary;
|
|
|
|
device->GL_GetProcAddress = KMSDRM_GLES_GetProcAddress;
|
|
|
|
device->GL_UnloadLibrary = KMSDRM_GLES_UnloadLibrary;
|
|
|
|
device->GL_CreateContext = KMSDRM_GLES_CreateContext;
|
|
|
|
device->GL_MakeCurrent = KMSDRM_GLES_MakeCurrent;
|
|
|
|
device->GL_SetSwapInterval = KMSDRM_GLES_SetSwapInterval;
|
|
|
|
device->GL_GetSwapInterval = KMSDRM_GLES_GetSwapInterval;
|
|
|
|
device->GL_SwapWindow = KMSDRM_GLES_SwapWindow;
|
|
|
|
device->GL_DeleteContext = KMSDRM_GLES_DeleteContext;
|
2017-08-04 23:00:47 +02:00
|
|
|
#endif
|
2017-08-02 19:22:48 +02:00
|
|
|
device->PumpEvents = KMSDRM_PumpEvents;
|
2020-02-09 20:44:22 +01:00
|
|
|
device->free = KMSDRM_DeleteDevice;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
|
|
|
return device;
|
|
|
|
|
|
|
|
cleanup:
|
2020-02-09 20:44:22 +01:00
|
|
|
if (device)
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_free(device);
|
2020-02-09 20:44:22 +01:00
|
|
|
if (viddata)
|
|
|
|
SDL_free(viddata);
|
2017-08-02 19:22:48 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
VideoBootStrap KMSDRM_bootstrap = {
|
|
|
|
"KMSDRM",
|
|
|
|
"KMS/DRM Video Driver",
|
|
|
|
KMSDRM_Available,
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_CreateDevice
|
2017-08-02 19:22:48 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
KMSDRM_FBDestroyCallback(struct gbm_bo *bo, void *data)
|
|
|
|
{
|
|
|
|
KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)data;
|
|
|
|
|
2019-03-13 14:54:51 +01:00
|
|
|
if (fb_info && fb_info->drm_fd >= 0 && fb_info->fb_id != 0) {
|
2017-08-02 19:22:48 +02:00
|
|
|
KMSDRM_drmModeRmFB(fb_info->drm_fd, fb_info->fb_id);
|
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Delete DRM FB %u", fb_info->fb_id);
|
|
|
|
}
|
|
|
|
|
2019-03-11 15:22:40 +01:00
|
|
|
SDL_free(fb_info);
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
KMSDRM_FBInfo *
|
|
|
|
KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo)
|
|
|
|
{
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
2020-02-15 01:17:17 +01:00
|
|
|
unsigned w,h;
|
|
|
|
int ret;
|
|
|
|
Uint32 stride, handle;
|
2020-02-09 20:44:22 +01:00
|
|
|
|
|
|
|
/* Check for an existing framebuffer */
|
|
|
|
KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)KMSDRM_gbm_bo_get_user_data(bo);
|
|
|
|
|
|
|
|
if (fb_info) {
|
2017-08-02 19:22:48 +02:00
|
|
|
return fb_info;
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
/* Create a structure that contains enough info to remove the framebuffer
|
|
|
|
when the backing buffer is destroyed */
|
2017-08-02 19:22:48 +02:00
|
|
|
fb_info = (KMSDRM_FBInfo *)SDL_calloc(1, sizeof(KMSDRM_FBInfo));
|
2020-02-09 20:44:22 +01:00
|
|
|
|
|
|
|
if (!fb_info) {
|
2017-08-05 22:10:36 +02:00
|
|
|
SDL_OutOfMemory();
|
|
|
|
return NULL;
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
fb_info->drm_fd = viddata->drm_fd;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
/* Create framebuffer object for the buffer */
|
2020-02-15 01:17:17 +01:00
|
|
|
w = KMSDRM_gbm_bo_get_width(bo);
|
|
|
|
h = KMSDRM_gbm_bo_get_height(bo);
|
|
|
|
stride = KMSDRM_gbm_bo_get_stride(bo);
|
|
|
|
handle = KMSDRM_gbm_bo_get_handle(bo).u32;
|
|
|
|
ret = KMSDRM_drmModeAddFB(viddata->drm_fd, w, h, 24, 32, stride, handle,
|
2020-02-09 20:44:22 +01:00
|
|
|
&fb_info->fb_id);
|
|
|
|
if (ret) {
|
|
|
|
SDL_free(fb_info);
|
|
|
|
return NULL;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
|
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "New DRM FB (%u): %ux%u, stride %u from BO %p",
|
|
|
|
fb_info->fb_id, w, h, stride, (void *)bo);
|
2017-08-02 19:22:48 +02:00
|
|
|
|
|
|
|
/* Associate our DRM framebuffer with this buffer object */
|
|
|
|
KMSDRM_gbm_bo_set_user_data(bo, fb_info, KMSDRM_FBDestroyCallback);
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
return fb_info;
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
static void
|
|
|
|
KMSDRM_FlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data)
|
|
|
|
{
|
Fixed bug 5140 - KMSDRM: Dynamic vsync toggle does not work
Manuel Alfayate Corchete
The KMSDRM backend was doing things wrong because of some small (but important) misconceptions on how KMS/DRM works: to implement a largely broken non-vsync refresh mechanism, the SwapWindow() function was issuing new pageflips before previous ones had completed, thus causing EBUSY returns, buffer mismanagement, etc... resulting in general breakage on vsync disabling from apps, that would not allow vsync to work again without KMSDRM video re-initialization.
To further clarify, on most DRM drivers async pageflips are NOT working nowadays, so all issued pageflips will complete on next VBLANK, NOT ASAP (calling drmModePageFlip() with the DRM_MODE_PAGE_FLIP_ASYNC flag will return error).
The old code was assuming that can just issue a synchronous (=on VBLANK) pageflip and then pass a 0 timeout to the pull() function so we do not wait for the pageflip event, thinking that this will lead to correct non-vsynced screen updates from the program: That is plain wrong.
Each pageflip has to be waite before issuing a new one, ALWAYS. And if we do not support ASYNC pageflips on the DRM driver level, then we are forced to wait for the next VBLANK. There is no way around it.
I have also added many comments on the KMSDRM code. This is needed for future reference for me or others who may need to look at this code: KMS/DRM terminology regarding what SYNC and ASYNC mean in pageflip terms, and where to do certain things and why, is not trivial. It is not desirable or possible to invest time on researching the same concepts every time there is need to dive into this code. So please leave all these comments in the patch.
2020-05-27 01:27:00 +02:00
|
|
|
/* If the data pointer received here is the same passed as the user_data in drmModePageFlip()
|
|
|
|
then this is the event handler for the pageflip that was issued on drmPageFlip(): got here
|
|
|
|
because of that precise page flip, the while loop gets broken here because of the right event.
|
|
|
|
This knowledge will allow handling different issued pageflips if sometime in the future
|
|
|
|
managing different CRTCs in SDL2 is needed, for example (synchronous pageflips happen on vblank
|
|
|
|
and vblank is a CRTC thing). */
|
2020-02-09 20:44:22 +01:00
|
|
|
*((SDL_bool *) data) = SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_bool
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout) {
|
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
|
drmEventContext ev = {0};
|
2020-02-15 01:17:17 +01:00
|
|
|
struct pollfd pfd = {0};
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
ev.version = DRM_EVENT_CONTEXT_VERSION;
|
|
|
|
ev.page_flip_handler = KMSDRM_FlipHandler;
|
|
|
|
|
|
|
|
pfd.fd = viddata->drm_fd;
|
|
|
|
pfd.events = POLLIN;
|
|
|
|
|
|
|
|
while (windata->waiting_for_flip) {
|
|
|
|
pfd.revents = 0;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (poll(&pfd, 1, timeout) < 0) {
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll error");
|
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (pfd.revents & (POLLHUP | POLLERR)) {
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll hup or error");
|
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
Fixed bug 5140 - KMSDRM: Dynamic vsync toggle does not work
Manuel Alfayate Corchete
The KMSDRM backend was doing things wrong because of some small (but important) misconceptions on how KMS/DRM works: to implement a largely broken non-vsync refresh mechanism, the SwapWindow() function was issuing new pageflips before previous ones had completed, thus causing EBUSY returns, buffer mismanagement, etc... resulting in general breakage on vsync disabling from apps, that would not allow vsync to work again without KMSDRM video re-initialization.
To further clarify, on most DRM drivers async pageflips are NOT working nowadays, so all issued pageflips will complete on next VBLANK, NOT ASAP (calling drmModePageFlip() with the DRM_MODE_PAGE_FLIP_ASYNC flag will return error).
The old code was assuming that can just issue a synchronous (=on VBLANK) pageflip and then pass a 0 timeout to the pull() function so we do not wait for the pageflip event, thinking that this will lead to correct non-vsynced screen updates from the program: That is plain wrong.
Each pageflip has to be waite before issuing a new one, ALWAYS. And if we do not support ASYNC pageflips on the DRM driver level, then we are forced to wait for the next VBLANK. There is no way around it.
I have also added many comments on the KMSDRM code. This is needed for future reference for me or others who may need to look at this code: KMS/DRM terminology regarding what SYNC and ASYNC mean in pageflip terms, and where to do certain things and why, is not trivial. It is not desirable or possible to invest time on researching the same concepts every time there is need to dive into this code. So please leave all these comments in the patch.
2020-05-27 01:27:00 +02:00
|
|
|
/* Is the fd readable? Thats enough to call drmHandleEvent() on it. */
|
2020-02-09 20:44:22 +01:00
|
|
|
if (pfd.revents & POLLIN) {
|
Fixed bug 5140 - KMSDRM: Dynamic vsync toggle does not work
Manuel Alfayate Corchete
The KMSDRM backend was doing things wrong because of some small (but important) misconceptions on how KMS/DRM works: to implement a largely broken non-vsync refresh mechanism, the SwapWindow() function was issuing new pageflips before previous ones had completed, thus causing EBUSY returns, buffer mismanagement, etc... resulting in general breakage on vsync disabling from apps, that would not allow vsync to work again without KMSDRM video re-initialization.
To further clarify, on most DRM drivers async pageflips are NOT working nowadays, so all issued pageflips will complete on next VBLANK, NOT ASAP (calling drmModePageFlip() with the DRM_MODE_PAGE_FLIP_ASYNC flag will return error).
The old code was assuming that can just issue a synchronous (=on VBLANK) pageflip and then pass a 0 timeout to the pull() function so we do not wait for the pageflip event, thinking that this will lead to correct non-vsynced screen updates from the program: That is plain wrong.
Each pageflip has to be waite before issuing a new one, ALWAYS. And if we do not support ASYNC pageflips on the DRM driver level, then we are forced to wait for the next VBLANK. There is no way around it.
I have also added many comments on the KMSDRM code. This is needed for future reference for me or others who may need to look at this code: KMS/DRM terminology regarding what SYNC and ASYNC mean in pageflip terms, and where to do certain things and why, is not trivial. It is not desirable or possible to invest time on researching the same concepts every time there is need to dive into this code. So please leave all these comments in the patch.
2020-05-27 01:27:00 +02:00
|
|
|
/* Page flip? ONLY if the event that made the fd readable (=POLLIN state)
|
|
|
|
is a page flip, will drmHandleEvent call page_flip_handler, which will break the loop.
|
|
|
|
The drmHandleEvent() and subsequent page_flip_handler calls are both synchronous (blocking),
|
|
|
|
nothing runs on a different thread, so no need to protect waiting_for_flip access with mutexes. */
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_drmHandleEvent(viddata->drm_fd, &ev);
|
2017-08-02 19:22:48 +02:00
|
|
|
} else {
|
|
|
|
/* Timed out and page flip didn't happen */
|
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Dropping frame while waiting_for_flip");
|
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
return SDL_TRUE;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* SDL Video and Display initialization/handling functions */
|
|
|
|
/* _this is a SDL_VideoDevice * */
|
|
|
|
/*****************************************************************************/
|
2020-02-27 17:20:34 +01:00
|
|
|
static void
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_DestroySurfaces(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
|
|
|
|
|
|
|
|
KMSDRM_WaitPageFlip(_this, windata, -1);
|
|
|
|
|
|
|
|
if (windata->curr_bo) {
|
|
|
|
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->curr_bo);
|
|
|
|
windata->curr_bo = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (windata->next_bo) {
|
|
|
|
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo);
|
|
|
|
windata->next_bo = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if SDL_VIDEO_OPENGL_EGL
|
|
|
|
SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
|
|
|
|
|
|
if (windata->egl_surface != EGL_NO_SURFACE) {
|
|
|
|
SDL_EGL_DestroySurface(_this, windata->egl_surface);
|
|
|
|
windata->egl_surface = EGL_NO_SURFACE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (windata->gs) {
|
|
|
|
KMSDRM_gbm_surface_destroy(windata->gs);
|
|
|
|
windata->gs = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
|
SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
|
|
|
|
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
|
|
|
|
Uint32 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;
|
2020-03-25 17:38:45 +01:00
|
|
|
#if SDL_VIDEO_OPENGL_EGL
|
2020-02-15 01:17:17 +01:00
|
|
|
EGLContext egl_context;
|
2020-03-25 17:38:45 +01:00
|
|
|
#endif
|
2020-02-09 20:44:22 +01:00
|
|
|
|
|
|
|
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm, surface_fmt, surface_flags)) {
|
|
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
|
|
|
|
}
|
|
|
|
|
|
|
|
#if SDL_VIDEO_OPENGL_EGL
|
|
|
|
SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
|
2020-02-15 01:17:17 +01:00
|
|
|
egl_context = (EGLContext)SDL_GL_GetCurrentContext();
|
2020-02-09 20:44:22 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
KMSDRM_DestroySurfaces(_this, window);
|
|
|
|
|
|
|
|
windata->gs = KMSDRM_gbm_surface_create(viddata->gbm, width, height, surface_fmt, surface_flags);
|
|
|
|
|
|
|
|
if (!windata->gs) {
|
|
|
|
return SDL_SetError("Could not create GBM surface");
|
|
|
|
}
|
|
|
|
|
|
|
|
#if SDL_VIDEO_OPENGL_EGL
|
|
|
|
windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
|
|
|
|
|
|
|
|
if (windata->egl_surface == EGL_NO_SURFACE) {
|
|
|
|
return SDL_SetError("Could not create EGL window surface");
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
|
|
|
|
|
|
|
|
windata->egl_surface_dirty = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
int
|
|
|
|
KMSDRM_VideoInit(_THIS)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
|
SDL_DisplayData *dispdata = NULL;
|
2017-08-02 19:22:48 +02:00
|
|
|
drmModeRes *resources = NULL;
|
|
|
|
drmModeEncoder *encoder = NULL;
|
2020-02-15 01:17:17 +01:00
|
|
|
char devname[32];
|
|
|
|
SDL_VideoDisplay display = {0};
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
|
|
|
|
|
|
|
|
if (!dispdata) {
|
2017-08-02 19:22:48 +02:00
|
|
|
return SDL_OutOfMemory();
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()");
|
|
|
|
|
|
|
|
/* Open /dev/dri/cardNN */
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_snprintf(devname, sizeof(devname), "/dev/dri/card%d", viddata->devindex);
|
|
|
|
|
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", devname);
|
|
|
|
viddata->drm_fd = open(devname, O_RDWR | O_CLOEXEC);
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (viddata->drm_fd < 0) {
|
|
|
|
ret = SDL_SetError("Could not open %s", devname);
|
2017-08-02 19:22:48 +02:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd);
|
|
|
|
|
|
|
|
viddata->gbm = KMSDRM_gbm_create_device(viddata->drm_fd);
|
|
|
|
if (!viddata->gbm) {
|
2017-08-02 19:22:48 +02:00
|
|
|
ret = SDL_SetError("Couldn't create gbm device.");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
/* Get all of the available connectors / devices / crtcs */
|
|
|
|
resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
|
2017-08-02 19:22:48 +02:00
|
|
|
if (!resources) {
|
2020-02-09 20:44:22 +01:00
|
|
|
ret = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd);
|
2017-08-02 19:22:48 +02:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
for (int i = 0; i < resources->count_connectors; i++) {
|
|
|
|
drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd, resources->connectors[i]);
|
|
|
|
|
|
|
|
if (!conn) {
|
2017-08-02 19:22:48 +02:00
|
|
|
continue;
|
2020-02-09 20:44:22 +01:00
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found connector %d with %d modes.",
|
2020-02-09 20:44:22 +01:00
|
|
|
conn->connector_id, conn->count_modes);
|
|
|
|
dispdata->conn = conn;
|
2017-08-02 19:22:48 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_drmModeFreeConnector(conn);
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (!dispdata->conn) {
|
2017-08-02 19:22:48 +02:00
|
|
|
ret = SDL_SetError("No currently active connector found.");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
/* Try to find the connector's current encoder */
|
|
|
|
for (int i = 0; i < resources->count_encoders; i++) {
|
|
|
|
encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
|
2018-12-01 19:09:00 +01:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (!encoder) {
|
|
|
|
continue;
|
2018-12-01 19:09:00 +01:00
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (encoder->encoder_id == dispdata->conn->encoder_id) {
|
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
|
2017-08-02 19:22:48 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
KMSDRM_drmModeFreeEncoder(encoder);
|
2017-08-11 19:05:45 +02:00
|
|
|
encoder = NULL;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (!encoder) {
|
|
|
|
/* No encoder was connected, find the first supported one */
|
|
|
|
for (int i = 0, j; i < resources->count_encoders; i++) {
|
|
|
|
encoder = KMSDRM_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) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j != dispdata->conn->count_encoders) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
KMSDRM_drmModeFreeEncoder(encoder);
|
|
|
|
encoder = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!encoder) {
|
2017-08-02 19:22:48 +02:00
|
|
|
ret = SDL_SetError("No connected encoder found.");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
|
2018-12-01 19:09:00 +01:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
/* Try to find a CRTC connected to this encoder */
|
|
|
|
dispdata->saved_crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
|
|
|
|
|
|
|
|
if (!dispdata->saved_crtc) {
|
|
|
|
/* No CRTC was connected, find the first CRTC that can be connected */
|
|
|
|
for (int i = 0; i < resources->count_crtcs; i++) {
|
2018-12-01 19:09:00 +01:00
|
|
|
if (encoder->possible_crtcs & (1 << i)) {
|
|
|
|
encoder->crtc_id = resources->crtcs[i];
|
2020-02-09 20:44:22 +01:00
|
|
|
dispdata->saved_crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
|
2018-12-01 19:09:00 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (!dispdata->saved_crtc) {
|
2017-08-02 19:22:48 +02:00
|
|
|
ret = SDL_SetError("No CRTC found.");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
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);
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
dispdata->crtc_id = encoder->crtc_id;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
/* 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
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
FIXME find first mode that specifies DRM_MODE_TYPE_PREFERRED */
|
|
|
|
dispdata->mode = dispdata->saved_crtc->mode;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup the single display that's available */
|
2020-02-15 01:17:17 +01:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
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
|
Fixed bug 5147 - KMSDRM: SetWindowFullscreen() failing with SDL_WINDOW_FULLSCREEN_DESKTOP
Manuel Alfayate Corchete
This patch is needed so programs that do this work as expected:
1) Start in a different video mode than the mode used by the system and then...
2) Try to go fullscreen with the mode originally used by the system via SetWindowFullScreen() with the SDL_WINDOW_FULLSCREEN_DESKTOP flag.
An example would be pt2-clone in https://github.com/8bitbubsy/pt2-clone.
This program does this:
Starts with:
video.window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, screenW, screenH, windowFlags);
and then, *IF* the user has configured it in fullscreen mode in its .ini, it tries to go fullscreen with the desktop mode:
SDL_SetWindowFullscreen(video.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
This sequence of operations is currently failing because SDL_SetDisplayModeForDisplay() in SDL_video.c fails because display->desktop_mode is not being initialized with its correct value: SetDisplayMode() in SDL_kmsdrmvideo.c will not be able to set the mode because it detects the mode to have a driverdata of 0x0 ("if (!modedata)") and rightfully returns an error.
So, the included patch fixes this small problem, and programs that first change the video mode and then try to go fullscreen with the system video mode will now work.
The patch simply fixes an small omission, but its really needed now that dynamic video mode changing was implemented on the KMSDRM backend.
2020-06-03 01:57:20 +02:00
|
|
|
|
|
|
|
/* DRM mode index for the desktop mode is needed to complete desktop mode init NOW,
|
|
|
|
so look for it in the DRM modes array. */
|
|
|
|
for (int i = 0; i < dispdata->conn->count_modes; i++) {
|
|
|
|
if (!SDL_memcmp(dispdata->conn->modes + i, &dispdata->saved_crtc->mode, sizeof(drmModeModeInfo))) {
|
|
|
|
SDL_DisplayModeData *modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData));
|
|
|
|
if (modedata) {
|
|
|
|
modedata->mode_index = i;
|
|
|
|
display.desktop_mode.driverdata = modedata;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
display.current_mode = display.desktop_mode;
|
|
|
|
display.driverdata = dispdata;
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_AddVideoDisplay(&display);
|
|
|
|
|
|
|
|
#ifdef SDL_INPUT_LINUXEV
|
|
|
|
SDL_EVDEV_Init();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
KMSDRM_InitMouse(_this);
|
|
|
|
|
2019-03-11 15:27:42 +01:00
|
|
|
return ret;
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
cleanup:
|
2020-02-09 20:44:22 +01:00
|
|
|
if (encoder)
|
2017-08-02 19:22:48 +02:00
|
|
|
KMSDRM_drmModeFreeEncoder(encoder);
|
2020-02-09 20:44:22 +01:00
|
|
|
if (resources)
|
2017-08-02 19:22:48 +02:00
|
|
|
KMSDRM_drmModeFreeResources(resources);
|
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
/* Error (complete) cleanup */
|
2020-02-09 20:44:22 +01:00
|
|
|
if (dispdata->conn) {
|
|
|
|
KMSDRM_drmModeFreeConnector(dispdata->conn);
|
|
|
|
dispdata->conn = NULL;
|
|
|
|
}
|
|
|
|
if (dispdata->saved_crtc) {
|
|
|
|
KMSDRM_drmModeFreeCrtc(dispdata->saved_crtc);
|
|
|
|
dispdata->saved_crtc = NULL;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
if (viddata->gbm) {
|
|
|
|
KMSDRM_gbm_device_destroy(viddata->gbm);
|
|
|
|
viddata->gbm = NULL;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
if (viddata->drm_fd >= 0) {
|
|
|
|
close(viddata->drm_fd);
|
|
|
|
viddata->drm_fd = -1;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_free(dispdata);
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
KMSDRM_VideoQuit(_THIS)
|
|
|
|
{
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
|
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
2017-08-02 19:22:48 +02:00
|
|
|
|
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoQuit()");
|
|
|
|
|
|
|
|
if (_this->gl_config.driver_loaded) {
|
|
|
|
SDL_GL_UnloadLibrary();
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
/* Clear out the window list */
|
|
|
|
SDL_free(viddata->windows);
|
|
|
|
viddata->windows = NULL;
|
|
|
|
viddata->max_windows = 0;
|
|
|
|
viddata->num_windows = 0;
|
|
|
|
|
|
|
|
/* Restore saved CRTC settings */
|
2020-03-02 23:55:40 +01:00
|
|
|
if (viddata->drm_fd >= 0 && dispdata && dispdata->conn && dispdata->saved_crtc) {
|
2020-02-09 20:44:22 +01:00
|
|
|
drmModeConnector *conn = dispdata->conn;
|
|
|
|
drmModeCrtc *crtc = dispdata->saved_crtc;
|
|
|
|
|
|
|
|
int ret = KMSDRM_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");
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-02 23:55:40 +01:00
|
|
|
if (dispdata && dispdata->conn) {
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_drmModeFreeConnector(dispdata->conn);
|
|
|
|
dispdata->conn = NULL;
|
|
|
|
}
|
2020-03-02 23:55:40 +01:00
|
|
|
if (dispdata && dispdata->saved_crtc) {
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_drmModeFreeCrtc(dispdata->saved_crtc);
|
|
|
|
dispdata->saved_crtc = NULL;
|
|
|
|
}
|
|
|
|
if (viddata->gbm) {
|
|
|
|
KMSDRM_gbm_device_destroy(viddata->gbm);
|
|
|
|
viddata->gbm = NULL;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
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;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
#ifdef SDL_INPUT_LINUXEV
|
|
|
|
SDL_EVDEV_Quit();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
|
|
|
|
{
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_DisplayData *dispdata = display->driverdata;
|
|
|
|
drmModeConnector *conn = dispdata->conn;
|
2020-02-15 01:17:17 +01:00
|
|
|
SDL_DisplayMode mode;
|
2020-02-09 20:44:22 +01:00
|
|
|
|
|
|
|
for (int i = 0; i < conn->count_modes; i++) {
|
|
|
|
SDL_DisplayModeData *modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData));
|
|
|
|
|
|
|
|
if (modedata) {
|
|
|
|
modedata->mode_index = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
mode.w = conn->modes[i].hdisplay;
|
|
|
|
mode.h = conn->modes[i].vdisplay;
|
|
|
|
mode.refresh_rate = conn->modes[i].vrefresh;
|
|
|
|
mode.format = SDL_PIXELFORMAT_ARGB8888;
|
|
|
|
mode.driverdata = modedata;
|
|
|
|
|
|
|
|
if (!SDL_AddDisplayMode(display, &mode)) {
|
|
|
|
SDL_free(modedata);
|
|
|
|
}
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
|
|
|
|
{
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
|
|
|
|
SDL_DisplayData *dispdata = (SDL_DisplayData *)display->driverdata;
|
|
|
|
SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
|
2020-02-15 01:17:17 +01:00
|
|
|
drmModeConnector *conn = dispdata->conn;
|
2020-02-09 20:44:22 +01:00
|
|
|
|
|
|
|
if (!modedata) {
|
|
|
|
return SDL_SetError("Mode doesn't have an associated index");
|
|
|
|
}
|
|
|
|
|
|
|
|
dispdata->mode = conn->modes[modedata->mode_index];
|
|
|
|
|
|
|
|
for (int i = 0; i < viddata->num_windows; i++) {
|
|
|
|
SDL_Window *window = viddata->windows[i];
|
|
|
|
SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
|
|
|
|
|
|
|
|
#if SDL_VIDEO_OPENGL_EGL
|
|
|
|
/* Can't recreate EGL surfaces right now, need to wait until SwapWindow
|
|
|
|
so the correct thread-local surface and context state are available */
|
|
|
|
windata->egl_surface_dirty = 1;
|
|
|
|
#else
|
|
|
|
if (KMSDRM_CreateSurfaces(_this, window)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Tell app about the resize */
|
|
|
|
SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, mode->w, mode->h);
|
|
|
|
}
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
KMSDRM_CreateWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
|
2020-02-15 01:17:17 +01:00
|
|
|
SDL_WindowData *windata;
|
2020-02-09 20:44:22 +01:00
|
|
|
|
|
|
|
#if SDL_VIDEO_OPENGL_EGL
|
|
|
|
if (!_this->egl_data) {
|
|
|
|
if (SDL_GL_LoadLibrary(NULL) < 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2017-08-02 19:22:48 +02:00
|
|
|
|
|
|
|
/* Allocate window internal data */
|
2020-02-15 01:17:17 +01:00
|
|
|
windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
|
2020-02-09 20:44:22 +01:00
|
|
|
|
|
|
|
if (!windata) {
|
2017-08-04 23:00:47 +02:00
|
|
|
SDL_OutOfMemory();
|
|
|
|
goto error;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
Fixed bug 5140 - KMSDRM: Dynamic vsync toggle does not work
Manuel Alfayate Corchete
The KMSDRM backend was doing things wrong because of some small (but important) misconceptions on how KMS/DRM works: to implement a largely broken non-vsync refresh mechanism, the SwapWindow() function was issuing new pageflips before previous ones had completed, thus causing EBUSY returns, buffer mismanagement, etc... resulting in general breakage on vsync disabling from apps, that would not allow vsync to work again without KMSDRM video re-initialization.
To further clarify, on most DRM drivers async pageflips are NOT working nowadays, so all issued pageflips will complete on next VBLANK, NOT ASAP (calling drmModePageFlip() with the DRM_MODE_PAGE_FLIP_ASYNC flag will return error).
The old code was assuming that can just issue a synchronous (=on VBLANK) pageflip and then pass a 0 timeout to the pull() function so we do not wait for the pageflip event, thinking that this will lead to correct non-vsynced screen updates from the program: That is plain wrong.
Each pageflip has to be waite before issuing a new one, ALWAYS. And if we do not support ASYNC pageflips on the DRM driver level, then we are forced to wait for the next VBLANK. There is no way around it.
I have also added many comments on the KMSDRM code. This is needed for future reference for me or others who may need to look at this code: KMS/DRM terminology regarding what SYNC and ASYNC mean in pageflip terms, and where to do certain things and why, is not trivial. It is not desirable or possible to invest time on researching the same concepts every time there is need to dive into this code. So please leave all these comments in the patch.
2020-05-27 01:27:00 +02:00
|
|
|
/* In case low-latency is wanted, double-buffered video will be used. We take note here */
|
2020-02-09 20:44:22 +01:00
|
|
|
windata->double_buffer = SDL_FALSE;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) {
|
|
|
|
windata->double_buffer = SDL_TRUE;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
/* Setup driver data for this window */
|
2020-06-19 19:37:14 +02:00
|
|
|
windata->viddata = viddata;
|
2020-02-09 20:44:22 +01:00
|
|
|
window->driverdata = windata;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (KMSDRM_CreateSurfaces(_this, window)) {
|
|
|
|
goto error;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
/* 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;
|
2017-10-21 13:20:57 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (!viddata->windows) {
|
|
|
|
SDL_OutOfMemory();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
viddata->windows[viddata->num_windows++] = window;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-03-07 17:48:04 +01:00
|
|
|
/* Focus on the newly created window */
|
|
|
|
SDL_SetMouseFocus(window);
|
|
|
|
SDL_SetKeyboardFocus(window);
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_DestroyWindow(_this, window);
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
KMSDRM_DestroyWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
|
2020-02-15 01:17:17 +01:00
|
|
|
SDL_VideoData *viddata;
|
2020-02-09 20:44:22 +01:00
|
|
|
if (!windata) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove from the internal window list */
|
2020-02-15 01:17:17 +01:00
|
|
|
viddata = windata->viddata;
|
2020-02-09 20:44:22 +01:00
|
|
|
|
|
|
|
for (int i = 0; i < viddata->num_windows; i++) {
|
|
|
|
if (viddata->windows[i] == window) {
|
|
|
|
viddata->num_windows--;
|
|
|
|
|
|
|
|
for (int j = i; j < viddata->num_windows; j++) {
|
|
|
|
viddata->windows[j] = viddata->windows[j + 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
|
|
|
|
KMSDRM_DestroySurfaces(_this, window);
|
|
|
|
|
|
|
|
window->driverdata = NULL;
|
|
|
|
|
|
|
|
SDL_free(windata);
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
KMSDRM_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
KMSDRM_SetWindowTitle(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_SetWindowPosition(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_SetWindowSize(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_ShowWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_HideWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_RaiseWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_MaximizeWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_MinimizeWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_RestoreWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* SDL Window Manager function */
|
|
|
|
/*****************************************************************************/
|
|
|
|
SDL_bool
|
|
|
|
KMSDRM_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info)
|
|
|
|
{
|
|
|
|
if (info->version.major <= SDL_MAJOR_VERSION) {
|
|
|
|
return SDL_TRUE;
|
|
|
|
} else {
|
|
|
|
SDL_SetError("application not compiled with SDL %d.%d\n",
|
|
|
|
SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
|
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Failed to get window manager information */
|
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* SDL_VIDEO_DRIVER_KMSDRM */
|
|
|
|
|
|
|
|
/* vi: set ts=4 sw=4 expandtab: */
|