|
@@ -41,6 +41,7 @@
|
|
|
#include "SDL_kmsdrmopengles.h"
|
|
|
#include "SDL_kmsdrmmouse.h"
|
|
|
#include "SDL_kmsdrmdyn.h"
|
|
|
+#include "SDL_kmsdrmvulkan.h"
|
|
|
#include <sys/stat.h>
|
|
|
#include <dirent.h>
|
|
|
#include <errno.h>
|
|
@@ -249,19 +250,20 @@ err:
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
-KMSDRM_DestroyDumbBuffer(_THIS, dumb_buffer *buffer)
|
|
|
+KMSDRM_DestroyDumbBuffer(_THIS, dumb_buffer **buffer)
|
|
|
{
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
|
|
|
struct drm_mode_destroy_dumb destroy = {
|
|
|
- .handle = buffer->gem_handles[0],
|
|
|
+ .handle = (*buffer)->gem_handles[0],
|
|
|
};
|
|
|
|
|
|
- KMSDRM_drmModeRmFB(viddata->drm_fd, buffer->fb_id);
|
|
|
+ KMSDRM_drmModeRmFB(viddata->drm_fd, (*buffer)->fb_id);
|
|
|
|
|
|
- munmap(buffer->dumb.mem, buffer->dumb.size);
|
|
|
+ munmap((*buffer)->dumb.mem, (*buffer)->dumb.size);
|
|
|
KMSDRM_drmIoctl(viddata->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
|
|
|
- free(buffer);
|
|
|
+ free(*buffer);
|
|
|
+ *buffer = NULL;
|
|
|
}
|
|
|
|
|
|
/* Using the CPU mapping, fill the dumb buffer with black pixels. */
|
|
@@ -304,7 +306,7 @@ static dumb_buffer *KMSDRM_CreateBuffer(_THIS)
|
|
|
return ret;
|
|
|
|
|
|
err:
|
|
|
- KMSDRM_DestroyDumbBuffer(_this, ret);
|
|
|
+ KMSDRM_DestroyDumbBuffer(_this, &ret);
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
@@ -389,17 +391,19 @@ void print_plane_info(_THIS, drmModePlanePtr plane)
|
|
|
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]);
|
|
|
+ for (int j = 0; j < props->count_props; j++) {
|
|
|
+
|
|
|
+ drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd, props->props[j]);
|
|
|
+
|
|
|
if ((strcmp(p->name, "type") == 0)) {
|
|
|
- type = props->prop_values[i];
|
|
|
+ type = props->prop_values[j];
|
|
|
}
|
|
|
+
|
|
|
KMSDRM_drmModeFreeProperty(p);
|
|
|
}
|
|
|
|
|
@@ -428,7 +432,7 @@ void print_plane_info(_THIS, drmModePlanePtr plane)
|
|
|
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++) {
|
|
|
+ for (int i = 0; i < resources->count_crtcs; i++) {
|
|
|
if (plane->possible_crtcs & (1 << i)) {
|
|
|
uint32_t crtc_id = resources->crtcs[i];
|
|
|
printf ("%d", crtc_id);
|
|
@@ -557,16 +561,19 @@ setup_plane(_THIS, struct plane **plane, uint32_t plane_type)
|
|
|
{
|
|
|
uint32_t plane_id;
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
+ int ret = 0;
|
|
|
|
|
|
*plane = SDL_calloc(1, sizeof(**plane));
|
|
|
if (!(*plane)) {
|
|
|
- return SDL_OutOfMemory();
|
|
|
+ ret = SDL_OutOfMemory();
|
|
|
+ goto cleanup;
|
|
|
}
|
|
|
|
|
|
/* Get plane ID. */
|
|
|
plane_id = get_plane_id(_this, plane_type);
|
|
|
|
|
|
if (!plane_id) {
|
|
|
+ ret = SDL_SetError("Invalid Plane ID");
|
|
|
goto cleanup;
|
|
|
}
|
|
|
|
|
@@ -582,7 +589,7 @@ setup_plane(_THIS, struct plane **plane, uint32_t plane_type)
|
|
|
sizeof(*(*plane)->props_info));
|
|
|
|
|
|
if ( !((*plane)->props_info) ) {
|
|
|
- SDL_OutOfMemory();
|
|
|
+ ret = SDL_OutOfMemory();
|
|
|
goto cleanup;
|
|
|
}
|
|
|
|
|
@@ -592,12 +599,15 @@ setup_plane(_THIS, struct plane **plane, uint32_t plane_type)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return 0;
|
|
|
-
|
|
|
cleanup:
|
|
|
- SDL_free(*plane);
|
|
|
- *plane = NULL;
|
|
|
- return -1;
|
|
|
+
|
|
|
+ if (ret != 0) {
|
|
|
+ if (*plane) {
|
|
|
+ SDL_free(*plane);
|
|
|
+ *plane = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/* Free a plane and it's props. */
|
|
@@ -673,7 +683,7 @@ int drm_atomic_commit(_THIS, SDL_bool blocking)
|
|
|
if (ret) {
|
|
|
SDL_SetError("Atomic commit failed, returned %d.", ret);
|
|
|
/* Uncomment this for fast-debugging */
|
|
|
- // printf("ATOMIC COMMIT FAILED: %d.\n", ret);
|
|
|
+ printf("ATOMIC COMMIT FAILED: %s.\n", strerror(errno));
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
@@ -769,8 +779,6 @@ KMSDRM_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hd
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
static SDL_VideoDevice *
|
|
|
KMSDRM_CreateDevice(int devindex)
|
|
|
{
|
|
@@ -846,7 +854,13 @@ KMSDRM_CreateDevice(int devindex)
|
|
|
#endif
|
|
|
device->PumpEvents = KMSDRM_PumpEvents;
|
|
|
device->free = KMSDRM_DeleteDevice;
|
|
|
-
|
|
|
+#if SDL_VIDEO_VULKAN
|
|
|
+ device->Vulkan_LoadLibrary = KMSDRM_Vulkan_LoadLibrary;
|
|
|
+ device->Vulkan_UnloadLibrary = KMSDRM_Vulkan_UnloadLibrary;
|
|
|
+ device->Vulkan_GetInstanceExtensions = KMSDRM_Vulkan_GetInstanceExtensions;
|
|
|
+ device->Vulkan_CreateSurface = KMSDRM_Vulkan_CreateSurface;
|
|
|
+ device->Vulkan_GetDrawableSize = KMSDRM_Vulkan_GetDrawableSize;
|
|
|
+#endif
|
|
|
return device;
|
|
|
|
|
|
cleanup:
|
|
@@ -915,8 +929,8 @@ KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo)
|
|
|
}
|
|
|
|
|
|
/* Create framebuffer object for the buffer.
|
|
|
- It's VERY important to note that fb_id is what we ise to set the FB_ID prop of a plane
|
|
|
- when using the ATOMIC interface, and we get fb_id it here. */
|
|
|
+ It's VERY important to note that fb_id is what we use to set the FB_ID prop
|
|
|
+ of a plane when using the ATOMIC interface, and we get the fb_id here. */
|
|
|
ret = KMSDRM_drmModeAddFB2(viddata->drm_fd, width, height, format,
|
|
|
handles, strides, offsets, &fb_info->fb_id, 0);
|
|
|
|
|
@@ -938,409 +952,774 @@ KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo)
|
|
|
/* _this is a SDL_VideoDevice * */
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
-/* Destroy the window surfaces and buffers. Have the PRIMARY PLANE
|
|
|
- disconnected from these buffers before doing so, or have the PRIMARY PLANE
|
|
|
- reading the original FB where the TTY lives, before doing this, or CRTC will
|
|
|
- be disconnected by the kernel. */
|
|
|
-void
|
|
|
-KMSDRM_DestroySurfaces(_THIS, SDL_Window *window)
|
|
|
-{
|
|
|
- SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
|
|
|
- SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
|
|
- KMSDRM_PlaneInfo plane_info = {0};
|
|
|
+/* Deinitializes the dispdata members needed for KMSDRM operation that are
|
|
|
+ inoffeensive for VK compatibility. */
|
|
|
+void KMSDRM_DisplayDataDeinit (_THIS, SDL_DisplayData *dispdata) {
|
|
|
+ /* Free connector */
|
|
|
+ if (dispdata && dispdata->connector) {
|
|
|
+ if (dispdata->connector->connector) {
|
|
|
+ KMSDRM_drmModeFreeConnector(dispdata->connector->connector);
|
|
|
+ dispdata->connector->connector = NULL;
|
|
|
+ }
|
|
|
+ if (dispdata->connector->props_info) {
|
|
|
+ SDL_free(dispdata->connector->props_info);
|
|
|
+ dispdata->connector->props_info = NULL;
|
|
|
+ }
|
|
|
+ SDL_free(dispdata->connector);
|
|
|
+ dispdata->connector = NULL;
|
|
|
+ }
|
|
|
|
|
|
-#if SDL_VIDEO_OPENGL_EGL
|
|
|
- EGLContext egl_context;
|
|
|
-#endif
|
|
|
+ /* Free CRTC */
|
|
|
+ if (dispdata && dispdata->crtc) {
|
|
|
+ if (dispdata->crtc->crtc) {
|
|
|
+ KMSDRM_drmModeFreeCrtc(dispdata->crtc->crtc);
|
|
|
+ dispdata->crtc->crtc = NULL;
|
|
|
+ }
|
|
|
+ if (dispdata->crtc->props_info) {
|
|
|
+ SDL_free(dispdata->crtc->props_info);
|
|
|
+ dispdata->crtc->props_info = NULL;
|
|
|
+ }
|
|
|
+ SDL_free(dispdata->crtc);
|
|
|
+ dispdata->crtc = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- /********************************************************************/
|
|
|
- /* BLOCK 1: protect the PRIMARY PLANE before destroying the buffers */
|
|
|
- /* it's using. */
|
|
|
- /********************************************************************/
|
|
|
+/* Initializes the dispdata members needed for KMSDRM operation that are
|
|
|
+ inoffeensive for VK compatibility, except we must leave the drm_fd
|
|
|
+ closed when we get to the end of this function.
|
|
|
+ This is to be called early, in VideoInit(), because it gets us
|
|
|
+ the videomode information, which SDL needs immediately after VideoInit(). */
|
|
|
+int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) {
|
|
|
+ SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
|
|
|
- plane_info.plane = dispdata->display_plane;
|
|
|
- plane_info.crtc_id = dispdata->crtc->crtc->crtc_id;
|
|
|
- plane_info.fb_id = dispdata->dumb_buffer->fb_id;
|
|
|
- plane_info.src_w = dispdata->mode.hdisplay;
|
|
|
- plane_info.src_h = dispdata->mode.vdisplay;
|
|
|
- plane_info.crtc_w = dispdata->mode.hdisplay;
|
|
|
- plane_info.crtc_h = dispdata->mode.vdisplay;
|
|
|
+ drmModeRes *resources = NULL;
|
|
|
+ drmModeEncoder *encoder = NULL;
|
|
|
+ drmModeConnector *connector = NULL;
|
|
|
+ drmModeCrtc *crtc = NULL;
|
|
|
|
|
|
- drm_atomic_set_plane_props(&plane_info);
|
|
|
+ char devname[32];
|
|
|
+ int ret = 0;
|
|
|
+ unsigned i,j;
|
|
|
|
|
|
- /* Issue blocking atomic commit. */
|
|
|
- if (drm_atomic_commit(_this, SDL_TRUE)) {
|
|
|
- SDL_SetError("Failed to issue atomic commit on window destruction.");
|
|
|
+ dispdata->atomic_flags = 0;
|
|
|
+ dispdata->atomic_req = NULL;
|
|
|
+ dispdata->kms_fence = NULL;
|
|
|
+ dispdata->gpu_fence = NULL;
|
|
|
+ dispdata->kms_out_fence_fd = -1;
|
|
|
+ dispdata->dumb_buffer = NULL;
|
|
|
+ dispdata->modeset_pending = SDL_FALSE;
|
|
|
+ dispdata->gbm_init = SDL_FALSE;
|
|
|
+
|
|
|
+ dispdata->display_plane = NULL;
|
|
|
+ dispdata->cursor_plane = NULL;
|
|
|
+
|
|
|
+ dispdata->cursor_bo = NULL;
|
|
|
+
|
|
|
+ /* Open /dev/dri/cardNN */
|
|
|
+ SDL_snprintf(viddata->devpath, sizeof(viddata->devpath), "/dev/dri/card%d", viddata->devindex);
|
|
|
+
|
|
|
+ SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", devname);
|
|
|
+ viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC);
|
|
|
+
|
|
|
+ if (viddata->drm_fd < 0) {
|
|
|
+ ret = SDL_SetError("Could not open %s", devname);
|
|
|
+ goto cleanup;
|
|
|
}
|
|
|
|
|
|
- /****************************************************************************/
|
|
|
- /* BLOCK 2: We can finally destroy the window GBM and EGL surfaces, and */
|
|
|
- /* GBM buffers now that the buffers are not being used by the PRIMARY PLANE */
|
|
|
- /* anymore. */
|
|
|
- /****************************************************************************/
|
|
|
+ SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd);
|
|
|
|
|
|
- /* Destroy the GBM surface and buffers. */
|
|
|
- if (windata->bo) {
|
|
|
- KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
|
|
|
- windata->bo = NULL;
|
|
|
+ /* Try ATOMIC compatibility */
|
|
|
+ ret = check_atomic_modesetting(viddata->drm_fd);
|
|
|
+ if (ret) {
|
|
|
+ ret = SDL_SetError("not compatible with atomic modesetting");
|
|
|
+ goto cleanup;
|
|
|
}
|
|
|
|
|
|
- if (windata->next_bo) {
|
|
|
- KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo);
|
|
|
- windata->next_bo = NULL;
|
|
|
+ /********************************************/
|
|
|
+ /* Block for enabling ATOMIC compatibility. */
|
|
|
+ /********************************************/
|
|
|
+
|
|
|
+ /* Set ATOMIC compatibility */
|
|
|
+ /* TODO: We have just tried ATOMIC compatibility, haven't we? */
|
|
|
+ ret = KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
|
|
+ if (ret) {
|
|
|
+ ret = SDL_SetError("no atomic modesetting support.");
|
|
|
+ goto cleanup;
|
|
|
}
|
|
|
|
|
|
- /* Destroy the EGL surface. */
|
|
|
-#if SDL_VIDEO_OPENGL_EGL
|
|
|
- /***************************************************************************/
|
|
|
- /* In this eglMakeCurrent() call, we disable the current EGL surface */
|
|
|
- /* because we're going to destroy it, but DON'T disable the EGL context, */
|
|
|
- /* because it won't be enabled again until the programs ask for a pageflip */
|
|
|
- /* so we get to SwapWindow(). */
|
|
|
- /* If we disable the context until then and a program tries to retrieve */
|
|
|
- /* the context version info before calling for a pageflip, the program */
|
|
|
- /* will get wrong info and we will be in trouble. */
|
|
|
- /***************************************************************************/
|
|
|
- egl_context = (EGLContext)SDL_GL_GetCurrentContext();
|
|
|
- SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, egl_context);
|
|
|
+ /* Set UNIVERSAL PLANES compatibility */
|
|
|
+ ret = KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
|
|
+ if (ret) {
|
|
|
+ ret = SDL_SetError("no universal planes support.");
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
|
|
|
- if (windata->egl_surface != EGL_NO_SURFACE) {
|
|
|
- SDL_EGL_DestroySurface(_this, windata->egl_surface);
|
|
|
- windata->egl_surface = EGL_NO_SURFACE;
|
|
|
- }
|
|
|
-#endif
|
|
|
+ /*******************************************/
|
|
|
+ /* Block for getting the ATOMIC resources. */
|
|
|
+ /*******************************************/
|
|
|
|
|
|
- if (windata->gs) {
|
|
|
- KMSDRM_gbm_surface_destroy(windata->gs);
|
|
|
- windata->gs = NULL;
|
|
|
+ /* Get all of the available connectors / devices / crtcs */
|
|
|
+ resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
|
|
|
+ if (!resources) {
|
|
|
+ ret = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd);
|
|
|
+ goto cleanup;
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-int
|
|
|
-KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
|
|
|
-{
|
|
|
- SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
- SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
|
|
|
- SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
|
|
|
- uint32_t surface_fmt = GBM_FORMAT_ARGB8888;
|
|
|
- uint32_t surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
|
|
|
- uint32_t width, height;
|
|
|
+ /* Iterate on the available connectors to find a connected connector. */
|
|
|
+ for (i = 0; i < resources->count_connectors; i++) {
|
|
|
+ drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd,
|
|
|
+ resources->connectors[i]);
|
|
|
|
|
|
- /* Destroy the surfaces and buffers before creating the new ones. */
|
|
|
- KMSDRM_DestroySurfaces(_this, window);
|
|
|
+ if (!conn) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
|
|
|
- ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
|
|
|
+ if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
|
|
|
+ SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found connector %d with %d modes.",
|
|
|
+ conn->connector_id, conn->count_modes);
|
|
|
+ connector = conn;
|
|
|
|
|
|
- width = dispdata->mode.hdisplay;
|
|
|
- height = dispdata->mode.vdisplay;
|
|
|
- } else {
|
|
|
- width = window->w;
|
|
|
- height = window->h;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ KMSDRM_drmModeFreeConnector(conn);
|
|
|
}
|
|
|
|
|
|
- if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, surface_fmt, surface_flags)) {
|
|
|
- SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
|
|
|
+ if (!connector) {
|
|
|
+ ret = SDL_SetError("No currently active connector found.");
|
|
|
+ goto cleanup;
|
|
|
}
|
|
|
|
|
|
- windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, width, height, surface_fmt, surface_flags);
|
|
|
+ /* Try to find the connector's current encoder */
|
|
|
+ for (i = 0; i < resources->count_encoders; i++) {
|
|
|
+ encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
|
|
|
|
|
|
- if (!windata->gs) {
|
|
|
- return SDL_SetError("Could not create GBM surface");
|
|
|
- }
|
|
|
+ if (!encoder) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
-#if SDL_VIDEO_OPENGL_EGL
|
|
|
- /* We can't get the EGL context yet because SDL_CreateRenderer has not been called,
|
|
|
- but we need an EGL surface NOW, or GL won't be able to render into any surface
|
|
|
- and we won't see the first frame. */
|
|
|
- SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
|
|
|
- windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
|
|
|
+ if (encoder->encoder_id == connector->encoder_id) {
|
|
|
+ SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- if (windata->egl_surface == EGL_NO_SURFACE) {
|
|
|
- return SDL_SetError("Could not create EGL window surface");
|
|
|
+ KMSDRM_drmModeFreeEncoder(encoder);
|
|
|
+ encoder = NULL;
|
|
|
}
|
|
|
|
|
|
- /* Take note that we're still missing the EGL contex,
|
|
|
- so we can get it in SwapWindow, when SDL_CreateRenderer()
|
|
|
- has already been called. */
|
|
|
- windata->egl_context_pending = SDL_TRUE;
|
|
|
-#endif
|
|
|
+ if (!encoder) {
|
|
|
+ /* No encoder was connected, find the first supported one */
|
|
|
+ for (i = 0; i < resources->count_encoders; i++) {
|
|
|
+ encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
|
|
|
|
|
|
- return 0;
|
|
|
-}
|
|
|
+ if (!encoder) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
-void
|
|
|
-KMSDRM_DestroyWindow(_THIS, SDL_Window *window)
|
|
|
-{
|
|
|
- SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
|
|
|
- SDL_VideoData *viddata;
|
|
|
- unsigned int i, j;
|
|
|
+ for (j = 0; j < connector->count_encoders; j++) {
|
|
|
+ if (connector->encoders[j] == encoder->encoder_id) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if (!windata) {
|
|
|
- return;
|
|
|
+ if (j != connector->count_encoders) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ KMSDRM_drmModeFreeEncoder(encoder);
|
|
|
+ encoder = NULL;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- KMSDRM_DestroySurfaces(_this, window);
|
|
|
+ if (!encoder) {
|
|
|
+ ret = SDL_SetError("No connected encoder found.");
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
|
|
|
- /********************************************/
|
|
|
- /* Remove from the internal SDL window list */
|
|
|
- /********************************************/
|
|
|
- viddata = windata->viddata;
|
|
|
+ SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
|
|
|
|
|
|
- for (i = 0; i < viddata->num_windows; i++) {
|
|
|
- if (viddata->windows[i] == window) {
|
|
|
- viddata->num_windows--;
|
|
|
+ /* Try to find a CRTC connected to this encoder */
|
|
|
+ crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
|
|
|
|
|
|
- for (j = i; j < viddata->num_windows; j++) {
|
|
|
- viddata->windows[j] = viddata->windows[j + 1];
|
|
|
+ /* If no CRTC was connected to the encoder, find the first CRTC
|
|
|
+ that is supported by the encoder, and use that. */
|
|
|
+ if (!crtc) {
|
|
|
+ for (i = 0; i < resources->count_crtcs; i++) {
|
|
|
+ if (encoder->possible_crtcs & (1 << i)) {
|
|
|
+ encoder->crtc_id = resources->crtcs[i];
|
|
|
+ crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
|
|
|
+ break;
|
|
|
}
|
|
|
-
|
|
|
- break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /*********************************************************************/
|
|
|
- /* Free the window driverdata. Bye bye, surface and buffer pointers! */
|
|
|
- /*********************************************************************/
|
|
|
- window->driverdata = NULL;
|
|
|
- SDL_free(windata);
|
|
|
-}
|
|
|
+ if (!crtc) {
|
|
|
+ ret = SDL_SetError("No CRTC found.");
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
|
|
|
-/*****************************************************************************/
|
|
|
-/* Reconfigure the window scaling parameters and re-construct it's surfaces, */
|
|
|
-/* without destroying the window itself. */
|
|
|
-/* To be used by SetWindowSize() and SetWindowFullscreen(). */
|
|
|
-/*****************************************************************************/
|
|
|
-static int
|
|
|
-KMSDRM_ReconfigureWindow( _THIS, SDL_Window * window) {
|
|
|
- SDL_WindowData *windata = window->driverdata;
|
|
|
- SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
|
|
|
- float ratio;
|
|
|
+ /* Figure out the default mode to be set. */
|
|
|
+ dispdata->mode = crtc->mode;
|
|
|
|
|
|
- if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
|
|
|
- ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
|
|
|
+ /* Find the connector's preferred mode, to be used in case the current mode
|
|
|
+ is not valid, or if restoring the current mode fails.
|
|
|
+ We can always count on the preferred mode! */
|
|
|
+ for (i = 0; i < connector->count_modes; i++) {
|
|
|
+ if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) {
|
|
|
+ dispdata->preferred_mode = connector->modes[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- windata->src_w = dispdata->mode.hdisplay;
|
|
|
- windata->src_h = dispdata->mode.vdisplay;
|
|
|
- windata->output_w = dispdata->mode.hdisplay;
|
|
|
- windata->output_h = dispdata->mode.vdisplay;
|
|
|
- windata->output_x = 0;
|
|
|
+ /* If the current CRTC's mode isn't valid, select the preferred
|
|
|
+ mode of the connector. */
|
|
|
+ if (crtc->mode_valid == 0) {
|
|
|
+ dispdata->mode = dispdata->preferred_mode;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dispdata->mode.hdisplay == 0 || dispdata->mode.vdisplay == 0 ) {
|
|
|
+ ret = SDL_SetError("Couldn't get a valid connector videomode.");
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Get CRTC properties */
|
|
|
+ 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));
|
|
|
|
|
|
- } else {
|
|
|
+ if (!dispdata->crtc->props_info) {
|
|
|
+ ret = SDL_OutOfMemory();
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
|
|
|
- /* Normal non-fullscreen windows are scaled using the CRTC,
|
|
|
- so get output (CRTC) size and position, for AR correction. */
|
|
|
- ratio = (float)window->w / (float)window->h;
|
|
|
- windata->src_w = window->w;
|
|
|
- windata->src_h = window->h;
|
|
|
- windata->output_w = dispdata->mode.vdisplay * ratio;
|
|
|
- windata->output_h = dispdata->mode.vdisplay;
|
|
|
- windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2;
|
|
|
+ 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,
|
|
|
+ connector->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 = SDL_OutOfMemory();
|
|
|
+ goto cleanup;
|
|
|
}
|
|
|
|
|
|
- if (KMSDRM_CreateSurfaces(_this, window)) {
|
|
|
- return -1;
|
|
|
- }
|
|
|
+ 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]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Store the connector and crtc for future use. This is all we keep from this function,
|
|
|
+ and these are just structs, inoffensive to VK. */
|
|
|
+ dispdata->connector->connector = connector;
|
|
|
+ dispdata->crtc->crtc = crtc;
|
|
|
+
|
|
|
+ /***********************************/
|
|
|
+ /* Block fpr Vulkan compatibility. */
|
|
|
+ /***********************************/
|
|
|
+
|
|
|
+ /* THIS IS FOR VULKAN! Leave the FD closed, so VK can work.
|
|
|
+ Will reopen this in CreateWindow, but only if requested a non-VK window. */
|
|
|
+ KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 0);
|
|
|
+ KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0);
|
|
|
+ close (viddata->drm_fd);
|
|
|
+ viddata->drm_fd = -1;
|
|
|
|
|
|
- return 0;
|
|
|
+cleanup:
|
|
|
+ // TODO Use it as a list to see if everything we use here is freed.
|
|
|
+ if (encoder)
|
|
|
+ KMSDRM_drmModeFreeEncoder(encoder);
|
|
|
+ if (resources)
|
|
|
+ KMSDRM_drmModeFreeResources(resources);
|
|
|
+ if (ret != 0) {
|
|
|
+ /* Error (complete) cleanup */
|
|
|
+ if (dispdata->connector->connector) {
|
|
|
+ KMSDRM_drmModeFreeConnector(dispdata->connector->connector);
|
|
|
+ dispdata->connector->connector = NULL;
|
|
|
+ }
|
|
|
+ if (dispdata->crtc->crtc) {
|
|
|
+ KMSDRM_drmModeFreeCrtc(dispdata->crtc->crtc);
|
|
|
+ dispdata->crtc->crtc = NULL;
|
|
|
+ }
|
|
|
+ if (viddata->drm_fd >= 0) {
|
|
|
+ close(viddata->drm_fd);
|
|
|
+ viddata->drm_fd = -1;
|
|
|
+ }
|
|
|
+ SDL_free(dispdata);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
+/* Init the Vulkan-INCOMPATIBLE stuff:
|
|
|
+ Reopen FD, create gbm dev, create dumb buffer and setup display plane.
|
|
|
+ This is to be called late, in WindowCreate(), and ONLY if this is not
|
|
|
+ a Vulkan window.
|
|
|
+ We are doing this so late to allow Vulkan to work if we build a VK window.
|
|
|
+ These things are incompatible with Vulkan, which accesses the same resources
|
|
|
+ internally so they must be free when trying to build a Vulkan surface.
|
|
|
+*/
|
|
|
int
|
|
|
-KMSDRM_VideoInit(_THIS)
|
|
|
+KMSDRM_GBMInit (_THIS, SDL_DisplayData *dispdata)
|
|
|
{
|
|
|
- unsigned int i, j;
|
|
|
+ SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
|
|
|
int ret = 0;
|
|
|
+
|
|
|
+ /* Reopen the FD! */
|
|
|
+ viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC);
|
|
|
+ KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
|
|
+ KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
|
|
+
|
|
|
+ /* Create aux dumb buffer. It's only useful to keep the PRIMARY PLANE occupied
|
|
|
+ when we destroy the GBM surface and it's KMS buffers, so not being able to
|
|
|
+ create it is not fatal. */
|
|
|
+ dispdata->dumb_buffer = KMSDRM_CreateBuffer(_this);
|
|
|
+ if (!dispdata->dumb_buffer) {
|
|
|
+ ret = SDL_SetError("can't create dumb buffer.");
|
|
|
+ } else {
|
|
|
+ /* Fill the dumb buffer with black pixels. */
|
|
|
+ KMSDRM_FillDumbBuffer(dispdata->dumb_buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Create the GBM device. */
|
|
|
+ viddata->gbm_dev = KMSDRM_gbm_create_device(viddata->drm_fd);
|
|
|
+ if (!viddata->gbm_dev) {
|
|
|
+ ret = SDL_SetError("Couldn't create gbm device.");
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Setup the display plane. ONLY do this after dispdata has the right
|
|
|
+ crtc and connector, because these are used in this function. */
|
|
|
+ ret = setup_plane(_this, &(dispdata->display_plane), DRM_PLANE_TYPE_PRIMARY);
|
|
|
+ if (ret) {
|
|
|
+ ret = SDL_SetError("can't find suitable display plane.");
|
|
|
+ }
|
|
|
+
|
|
|
+ dispdata->gbm_init = SDL_TRUE;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/* Deinit the Vulkan-incompatible KMSDRM stuff. */
|
|
|
+void
|
|
|
+KMSDRM_GBMDeinit (_THIS, SDL_DisplayData *dispdata)
|
|
|
+{
|
|
|
+ KMSDRM_PlaneInfo plane_info = {0};
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
- SDL_DisplayData *dispdata = NULL;
|
|
|
- drmModeRes *resources = NULL;
|
|
|
- drmModeEncoder *encoder = NULL;
|
|
|
- char devname[32];
|
|
|
- SDL_VideoDisplay display = {0};
|
|
|
+ drmModeModeInfo mode;
|
|
|
|
|
|
- viddata->video_init = SDL_FALSE;
|
|
|
+ uint32_t blob_id;
|
|
|
|
|
|
- dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
|
|
|
- if (!dispdata) {
|
|
|
- return SDL_OutOfMemory();
|
|
|
+ /*****************************************************************/
|
|
|
+ /* */
|
|
|
+ /* BLOCK to safely destroy the DUMB BUFFER. */
|
|
|
+ /* */
|
|
|
+ /* We come from DestroyWindow(), where both DestroySurfaces() */
|
|
|
+ /* and this GBMDeinit() are called, one after another. */
|
|
|
+ /* So the GBM/EGL surfaces and buffers of the windows have */
|
|
|
+ /* already been destroyed already and, because of that, the */
|
|
|
+ /* PRIMARY PLANE is using the DUMB BUFFER. BUT the DUMB BUFFER */
|
|
|
+ /* we use to keep the PRIMARY PLANE occupied when we do */
|
|
|
+ /* DestroySurfaces calls is going to be destroyed one way or */
|
|
|
+ /* another when the program quits so, to prevent the the kernel */
|
|
|
+ /* from disabling the CRTC when it detects the deletion of a */
|
|
|
+ /* buffer that IS IN USE BY THE PRIMARY PLANE, we do one of */
|
|
|
+ /* these: */
|
|
|
+ /* */
|
|
|
+ /* -In AMDGPU, where manually disabling the CRTC and */
|
|
|
+ /* disconnecting the CONNECTOR from the CRTC is an */
|
|
|
+ /* unrecoverable situation, we point the PRIMARY PLANE to */
|
|
|
+ /* the original TTY buffer (not guaranteed to be there for us!) */
|
|
|
+ /* and then destroy the DUMB BUFFER). */
|
|
|
+ /* */
|
|
|
+ /* -In other drivers, we 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 destroy the DUMB BUFFER. */
|
|
|
+ /* We can leave all like this if we are exiting the program: */
|
|
|
+ /* FBCON or whatever will reconfigure things as it needs. */
|
|
|
+ /* */
|
|
|
+ /*****************************************************************/
|
|
|
+
|
|
|
+ /* dispdata->crtc->crtc->mode is the original video mode that was
|
|
|
+ configured on the connector when we lauched the program,
|
|
|
+ dispdata->mode is the current video mode, which could be different,
|
|
|
+ and dispdata->preferred_mode is the native display mode. */
|
|
|
+ mode = dispdata->crtc->crtc->mode;
|
|
|
+
|
|
|
+#if AMDGPU_COMPAT
|
|
|
+ /* This won't work if the console wasn't correctly restored when a prevous
|
|
|
+ program exited, because in that case the TTY buffer won't be in
|
|
|
+ crtc->crtc->buffer_id, so the following atomic commit will fail. */
|
|
|
+ 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 = mode.hdisplay;
|
|
|
+ plane_info.src_h = mode.vdisplay;
|
|
|
+ plane_info.crtc_w = mode.hdisplay;
|
|
|
+ plane_info.crtc_h = mode.vdisplay;
|
|
|
+
|
|
|
+ drm_atomic_set_plane_props(&plane_info);
|
|
|
+#else
|
|
|
+ add_connector_property(dispdata->atomic_req, dispdata->connector , "CRTC_ID", 0);
|
|
|
+ add_crtc_property(dispdata->atomic_req, dispdata->crtc , "ACTIVE", 0);
|
|
|
+
|
|
|
+ /* Since we initialize plane_info to all zeros,
|
|
|
+ ALL PRIMARY PLANE props are set to 0 with this,
|
|
|
+ including FB_ID and CRTC_ID.
|
|
|
+ Not all drivers like FB_ID and CRTC_ID to 0 yet. */
|
|
|
+ plane_info.plane = dispdata->display_plane;
|
|
|
+ drm_atomic_set_plane_props(&plane_info);
|
|
|
+#endif
|
|
|
+
|
|
|
+ /* Set the props that restore the original video mode. */
|
|
|
+ dispdata->atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
|
|
+ add_connector_property(dispdata->atomic_req, dispdata->connector, "CRTC_ID", dispdata->crtc->crtc->crtc_id);
|
|
|
+ KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &mode, sizeof(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);
|
|
|
+
|
|
|
+ /* Issue blocking atomic commit. */
|
|
|
+ if (drm_atomic_commit(_this, SDL_TRUE)) {
|
|
|
+ SDL_SetError("Failed to issue atomic commit on video quitting.");
|
|
|
}
|
|
|
|
|
|
- dispdata->display_plane = SDL_calloc(1, sizeof(*dispdata->display_plane));
|
|
|
- dispdata->crtc = SDL_calloc(1, sizeof(*dispdata->crtc));
|
|
|
- dispdata->connector = SDL_calloc(1, sizeof(*dispdata->connector));
|
|
|
- if (!(dispdata->display_plane) || !(dispdata->crtc) || !(dispdata->connector)) {
|
|
|
- return SDL_OutOfMemory();
|
|
|
+ /* Destroy the DUMB buffer if it exists, now that it's not being
|
|
|
+ used anymore by the PRIMARY PLANE. */
|
|
|
+ if (dispdata->dumb_buffer) {
|
|
|
+ KMSDRM_DestroyDumbBuffer(_this, &dispdata->dumb_buffer);
|
|
|
}
|
|
|
|
|
|
- dispdata->atomic_flags = 0;
|
|
|
- dispdata->atomic_req = NULL;
|
|
|
- dispdata->kms_fence = NULL;
|
|
|
- dispdata->gpu_fence = NULL;
|
|
|
- dispdata->kms_out_fence_fd = -1;
|
|
|
- dispdata->dumb_buffer = NULL;
|
|
|
- dispdata->modeset_pending = SDL_FALSE;
|
|
|
+ /*************************************************/
|
|
|
+ /* BLOCK to safely destroy the dumb buffer ENDS. */
|
|
|
+ /*************************************************/
|
|
|
+
|
|
|
+ /* Free display plane */
|
|
|
+ free_plane(&dispdata->display_plane);
|
|
|
|
|
|
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()");
|
|
|
+ /* Free cursor plane (if still not freed) */
|
|
|
+ free_plane(&dispdata->cursor_plane);
|
|
|
|
|
|
- /* Open /dev/dri/cardNN */
|
|
|
- SDL_snprintf(devname, sizeof(devname), "/dev/dri/card%d", viddata->devindex);
|
|
|
+ /* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(),
|
|
|
+ already called when we get here. */
|
|
|
+ if (viddata->gbm_dev) {
|
|
|
+ KMSDRM_gbm_device_destroy(viddata->gbm_dev);
|
|
|
+ viddata->gbm_dev = NULL;
|
|
|
+ }
|
|
|
|
|
|
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", devname);
|
|
|
- viddata->drm_fd = open(devname, O_RDWR | O_CLOEXEC);
|
|
|
+ /* Finally close DRM FD. May be reopen on next non-vulkan window creation. */
|
|
|
+ if (viddata->drm_fd >= 0) {
|
|
|
+ close(viddata->drm_fd);
|
|
|
+ viddata->drm_fd = -1;
|
|
|
+ }
|
|
|
|
|
|
- if (viddata->drm_fd < 0) {
|
|
|
- ret = SDL_SetError("Could not open %s", devname);
|
|
|
- goto cleanup;
|
|
|
+ dispdata->gbm_init = SDL_FALSE;
|
|
|
+}
|
|
|
+
|
|
|
+/* Destroy the window surfaces and buffers. Have the PRIMARY PLANE
|
|
|
+ disconnected from these buffers before doing so, or have the PRIMARY PLANE
|
|
|
+ reading the original FB where the TTY lives, before doing this, or CRTC will
|
|
|
+ be disconnected by the kernel. */
|
|
|
+void
|
|
|
+KMSDRM_DestroySurfaces(_THIS, SDL_Window *window)
|
|
|
+{
|
|
|
+ SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
|
|
|
+ SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
|
|
+ KMSDRM_PlaneInfo plane_info = {0};
|
|
|
+
|
|
|
+#if SDL_VIDEO_OPENGL_EGL
|
|
|
+ EGLContext egl_context;
|
|
|
+#endif
|
|
|
+
|
|
|
+ /********************************************************************/
|
|
|
+ /* BLOCK 1: protect the PRIMARY PLANE before destroying the buffers */
|
|
|
+ /* it's using, by making it point to the dumb buffer. */
|
|
|
+ /********************************************************************/
|
|
|
+
|
|
|
+ plane_info.plane = dispdata->display_plane;
|
|
|
+ plane_info.crtc_id = dispdata->crtc->crtc->crtc_id;
|
|
|
+ plane_info.fb_id = dispdata->dumb_buffer->fb_id;
|
|
|
+ plane_info.src_w = dispdata->mode.hdisplay;
|
|
|
+ plane_info.src_h = dispdata->mode.vdisplay;
|
|
|
+ plane_info.crtc_w = dispdata->mode.hdisplay;
|
|
|
+ plane_info.crtc_h = dispdata->mode.vdisplay;
|
|
|
+
|
|
|
+ drm_atomic_set_plane_props(&plane_info);
|
|
|
+
|
|
|
+ /* Issue blocking atomic commit. */
|
|
|
+ if (drm_atomic_commit(_this, SDL_TRUE)) {
|
|
|
+ SDL_SetError("Failed to issue atomic commit on surfaces destruction.");
|
|
|
}
|
|
|
|
|
|
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd);
|
|
|
+ /****************************************************************************/
|
|
|
+ /* BLOCK 2: We can finally destroy the window GBM and EGL surfaces, and */
|
|
|
+ /* GBM buffers now that the buffers are not being used by the PRIMARY PLANE */
|
|
|
+ /* anymore. */
|
|
|
+ /****************************************************************************/
|
|
|
|
|
|
- /* Try ATOMIC compatibility */
|
|
|
+ /* Destroy the GBM surface and buffers. */
|
|
|
+ if (windata->bo) {
|
|
|
+ KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
|
|
|
+ windata->bo = NULL;
|
|
|
+ }
|
|
|
|
|
|
- ret = check_atomic_modesetting(viddata->drm_fd);
|
|
|
- if (ret) {
|
|
|
- goto cleanup;
|
|
|
+ if (windata->next_bo) {
|
|
|
+ KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo);
|
|
|
+ windata->next_bo = NULL;
|
|
|
}
|
|
|
|
|
|
- ret = KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
|
|
- if (ret) {
|
|
|
- ret = SDL_SetError("no universal planes support.");
|
|
|
- goto cleanup;
|
|
|
+
|
|
|
+ /***************************************************************************/
|
|
|
+ /* Destroy the EGL surface. */
|
|
|
+ /* In this eglMakeCurrent() call, we disable the current EGL surface */
|
|
|
+ /* because we're going to destroy it, but DON'T disable the EGL context, */
|
|
|
+ /* because it won't be enabled again until the programs ask for a pageflip */
|
|
|
+ /* so we get to SwapWindow(). */
|
|
|
+ /* If we disable the context until then and a program tries to retrieve */
|
|
|
+ /* the context version info before calling for a pageflip, the program */
|
|
|
+ /* will get wrong info and we will be in trouble. */
|
|
|
+ /***************************************************************************/
|
|
|
+
|
|
|
+#if SDL_VIDEO_OPENGL_EGL
|
|
|
+ egl_context = (EGLContext)SDL_GL_GetCurrentContext();
|
|
|
+ SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, egl_context);
|
|
|
+
|
|
|
+ if (windata->egl_surface != EGL_NO_SURFACE) {
|
|
|
+ SDL_EGL_DestroySurface(_this, windata->egl_surface);
|
|
|
+ windata->egl_surface = EGL_NO_SURFACE;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (windata->gs) {
|
|
|
+ KMSDRM_gbm_surface_destroy(windata->gs);
|
|
|
+ windata->gs = NULL;
|
|
|
}
|
|
|
|
|
|
- /* Create the GBM device */
|
|
|
+}
|
|
|
|
|
|
- viddata->gbm_dev = KMSDRM_gbm_create_device(viddata->drm_fd);
|
|
|
- if (!viddata->gbm_dev) {
|
|
|
- ret = SDL_SetError("Couldn't create gbm device.");
|
|
|
- goto cleanup;
|
|
|
+int
|
|
|
+KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
|
|
|
+{
|
|
|
+ SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
+ SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
|
|
|
+ SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
|
|
|
+ uint32_t surface_fmt = GBM_FORMAT_ARGB8888;
|
|
|
+ uint32_t surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
|
|
|
+ uint32_t width, height;
|
|
|
+
|
|
|
+ EGLContext egl_context;
|
|
|
+
|
|
|
+ /* Destroy the surfaces and buffers before creating the new ones. */
|
|
|
+ // TODO REENABLE THIS IF CGENIUS FAILS BECAUSE IT CREATES A NEW WINDOW
|
|
|
+ // W/O DESTRYING THE PREVIOUS ONE.
|
|
|
+ //KMSDRM_DestroySurfaces(_this, window);
|
|
|
+
|
|
|
+ if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
|
|
|
+ ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
|
|
|
+
|
|
|
+ width = dispdata->mode.hdisplay;
|
|
|
+ height = dispdata->mode.vdisplay;
|
|
|
+ } else {
|
|
|
+ width = window->w;
|
|
|
+ height = window->h;
|
|
|
}
|
|
|
|
|
|
- /* Get all of the available connectors / devices / crtcs */
|
|
|
- resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
|
|
|
- if (!resources) {
|
|
|
- ret = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd);
|
|
|
- goto cleanup;
|
|
|
+ if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, surface_fmt, surface_flags)) {
|
|
|
+ SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
|
|
|
}
|
|
|
|
|
|
- /* Iterate on the available connectors to find a connected connector. */
|
|
|
- for (i = 0; i < resources->count_connectors; i++) {
|
|
|
- drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd, resources->connectors[i]);
|
|
|
+ windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, width, height, surface_fmt, surface_flags);
|
|
|
|
|
|
- if (!conn) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ if (!windata->gs) {
|
|
|
+ return SDL_SetError("Could not create GBM surface");
|
|
|
+ }
|
|
|
|
|
|
- if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
|
|
|
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found connector %d with %d modes.",
|
|
|
- conn->connector_id, conn->count_modes);
|
|
|
- dispdata->connector->connector = conn;
|
|
|
- break;
|
|
|
- }
|
|
|
+#if SDL_VIDEO_OPENGL_EGL //TODO Restore this lo LibraryLoad and Unload calls.
|
|
|
+ /* We can't get the EGL context yet because SDL_CreateRenderer has not been called,
|
|
|
+ but we need an EGL surface NOW, or GL won't be able to render into any surface
|
|
|
+ and we won't see the first frame. */
|
|
|
+ SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
|
|
|
+ windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
|
|
|
|
|
|
- KMSDRM_drmModeFreeConnector(conn);
|
|
|
+ if (windata->egl_surface == EGL_NO_SURFACE) {
|
|
|
+ return SDL_SetError("Could not create EGL window surface");
|
|
|
}
|
|
|
|
|
|
- if (!dispdata->connector->connector) {
|
|
|
- ret = SDL_SetError("No currently active connector found.");
|
|
|
- goto cleanup;
|
|
|
+ /* Current context passing to EGL is now done here. If something fails,
|
|
|
+ go back to delayed SDL_EGL_MakeCurrent() call in SwapWindow. */
|
|
|
+ /* TODO Errorcheck on this (may lead to delayed call again...) */
|
|
|
+ egl_context = (EGLContext)SDL_GL_GetCurrentContext();
|
|
|
+ SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+KMSDRM_DestroyWindow(_THIS, SDL_Window *window)
|
|
|
+{
|
|
|
+ SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
|
|
|
+ SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
|
|
|
+ SDL_VideoData *viddata = windata->viddata;
|
|
|
+ SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
|
|
|
+ unsigned int i, j;
|
|
|
+
|
|
|
+ if (!windata) {
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- /* Try to find the connector's current encoder */
|
|
|
- for (i = 0; i < resources->count_encoders; i++) {
|
|
|
- encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
|
|
|
+ if (!is_vulkan) {
|
|
|
|
|
|
- if (!encoder) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ KMSDRM_DestroySurfaces(_this, window);
|
|
|
|
|
|
- if (encoder->encoder_id == dispdata->connector->connector->encoder_id) {
|
|
|
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
|
|
|
- break;
|
|
|
+ KMSDRM_DeinitMouse(_this);
|
|
|
+
|
|
|
+ if (_this->egl_data) {
|
|
|
+ SDL_EGL_UnloadLibrary(_this);
|
|
|
}
|
|
|
|
|
|
- KMSDRM_drmModeFreeEncoder(encoder);
|
|
|
- encoder = NULL;
|
|
|
+ if (dispdata->gbm_init) {
|
|
|
+ KMSDRM_GBMDeinit(_this, dispdata);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if (!encoder) {
|
|
|
- /* No encoder was connected, find the first supported one */
|
|
|
- for (i = 0; i < resources->count_encoders; i++) {
|
|
|
- encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
|
|
|
+#if AMDGPU_COMPAT
|
|
|
+ /* Since vkDestroySurfaceKHR does not destroy the native surface (only the Vulkan one),
|
|
|
+ runnin Vulkan programs leave the last buffer connected to the primary plane,
|
|
|
+ at least on AMDGPU, and the TTY buffer isn't restored.
|
|
|
+ That's not a big problem, but running a GLES program after that will cause the
|
|
|
+ atomic commit we do for restoring the TTY buffer to fail, because the TTY buffer
|
|
|
+ isn't there when we launch the GLES program.
|
|
|
+ So what we do here is "hack" Vulkan and restore the TTY buffer here, as if
|
|
|
+ we were running a GLES program. We get here after the program's vkDestroySurfaceKHR
|
|
|
+ has been called, which allows us to think as if we were in the beginig
|
|
|
+ of a GLES program.
|
|
|
+ THIS IS DONE IN A SEPARATE BLOCK FOR POSSIBLE EASY FUTURE REMOVAL. DON'T OPTIMIZE. */
|
|
|
+
|
|
|
+ if (is_vulkan) {
|
|
|
+ KMSDRM_GBMInit(_this, dispdata);
|
|
|
+ KMSDRM_GBMDeinit(_this, dispdata);
|
|
|
+ }
|
|
|
+#endif
|
|
|
|
|
|
- if (!encoder) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ /********************************************/
|
|
|
+ /* Remove from the internal SDL window list */
|
|
|
+ /********************************************/
|
|
|
|
|
|
- for (j = 0; j < dispdata->connector->connector->count_encoders; j++) {
|
|
|
- if (dispdata->connector->connector->encoders[j] == encoder->encoder_id) {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ for (i = 0; i < viddata->num_windows; i++) {
|
|
|
+ if (viddata->windows[i] == window) {
|
|
|
+ viddata->num_windows--;
|
|
|
|
|
|
- if (j != dispdata->connector->connector->count_encoders) {
|
|
|
- break;
|
|
|
+ for (j = i; j < viddata->num_windows; j++) {
|
|
|
+ viddata->windows[j] = viddata->windows[j + 1];
|
|
|
}
|
|
|
|
|
|
- KMSDRM_drmModeFreeEncoder(encoder);
|
|
|
- encoder = NULL;
|
|
|
- }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*********************************************************************/
|
|
|
+ /* Free the window driverdata. Bye bye, surface and buffer pointers! */
|
|
|
+ /*********************************************************************/
|
|
|
+ window->driverdata = NULL;
|
|
|
+ SDL_free(windata);
|
|
|
+}
|
|
|
+
|
|
|
+/*****************************************************************************/
|
|
|
+/* Reconfigure the window scaling parameters and re-construct it's surfaces, */
|
|
|
+/* without destroying the window itself. */
|
|
|
+/* To be used by SetWindowSize() and SetWindowFullscreen(). */
|
|
|
+/*****************************************************************************/
|
|
|
+static int
|
|
|
+KMSDRM_ReconfigureWindow( _THIS, SDL_Window * window) {
|
|
|
+ SDL_WindowData *windata = window->driverdata;
|
|
|
+ SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
|
|
|
+ SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
|
|
|
+ float ratio;
|
|
|
+
|
|
|
+ if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
|
|
|
+ ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
|
|
|
+
|
|
|
+ windata->src_w = dispdata->mode.hdisplay;
|
|
|
+ windata->src_h = dispdata->mode.vdisplay;
|
|
|
+ windata->output_w = dispdata->mode.hdisplay;
|
|
|
+ windata->output_h = dispdata->mode.vdisplay;
|
|
|
+ windata->output_x = 0;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ /* Normal non-fullscreen windows are scaled using the CRTC,
|
|
|
+ so get output (CRTC) size and position, for AR correction. */
|
|
|
+ ratio = (float)window->w / (float)window->h;
|
|
|
+ windata->src_w = window->w;
|
|
|
+ windata->src_h = window->h;
|
|
|
+ windata->output_w = dispdata->mode.vdisplay * ratio;
|
|
|
+ windata->output_h = dispdata->mode.vdisplay;
|
|
|
+ windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
- if (!encoder) {
|
|
|
- ret = SDL_SetError("No connected encoder found.");
|
|
|
- goto cleanup;
|
|
|
+ if (!is_vulkan) {
|
|
|
+ if (KMSDRM_CreateSurfaces(_this, window)) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
}
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
|
|
|
+int
|
|
|
+KMSDRM_VideoInit(_THIS)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
|
|
|
- /* Try to find a CRTC connected to this encoder */
|
|
|
- dispdata->crtc->crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
|
|
|
+ SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
+ SDL_DisplayData *dispdata = NULL;
|
|
|
+ SDL_VideoDisplay display = {0};
|
|
|
|
|
|
- /* If no CRTC was connected to the encoder, find the first CRTC that is supported by the encoder, and use that. */
|
|
|
- if (!dispdata->crtc->crtc) {
|
|
|
- for (i = 0; i < resources->count_crtcs; i++) {
|
|
|
- if (encoder->possible_crtcs & (1 << i)) {
|
|
|
- encoder->crtc_id = resources->crtcs[i];
|
|
|
- dispdata->crtc->crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()");
|
|
|
+
|
|
|
+ viddata->video_init = SDL_FALSE;
|
|
|
+
|
|
|
+ dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
|
|
|
+ if (!dispdata) {
|
|
|
+ return SDL_OutOfMemory();
|
|
|
}
|
|
|
|
|
|
- if (!dispdata->crtc->crtc) {
|
|
|
- ret = SDL_SetError("No CRTC found.");
|
|
|
+ /* Alloc memory for these. */
|
|
|
+ dispdata->display_plane = SDL_calloc(1, sizeof(*dispdata->display_plane));
|
|
|
+ dispdata->crtc = SDL_calloc(1, sizeof(*dispdata->crtc));
|
|
|
+ dispdata->connector = SDL_calloc(1, sizeof(*dispdata->connector));
|
|
|
+ if (!(dispdata->display_plane) || !(dispdata->crtc) || !(dispdata->connector)) {
|
|
|
+ ret = SDL_OutOfMemory();
|
|
|
goto cleanup;
|
|
|
}
|
|
|
|
|
|
- /* Figure out the default mode to be set. If the current CRTC's mode isn't
|
|
|
- valid, select the first mode supported by the connector
|
|
|
-
|
|
|
- FIXME find first mode that specifies DRM_MODE_TYPE_PREFERRED */
|
|
|
- dispdata->mode = dispdata->crtc->crtc->mode;
|
|
|
+ /* Setup the single display that's available.
|
|
|
+ There's no problem with it being still incomplete. */
|
|
|
+ display.driverdata = dispdata;
|
|
|
|
|
|
- if (dispdata->crtc->crtc->mode_valid == 0) {
|
|
|
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO,
|
|
|
- "Current mode is invalid, selecting connector's mode #0.");
|
|
|
- dispdata->mode = dispdata->connector->connector->modes[0];
|
|
|
+ /* Get KMSDRM resources info and store what we need. Getting and storing
|
|
|
+ this info isn't a problem for VK compatibility.
|
|
|
+ For VK-incompatible initializations we have KMSDRM_GBMInit(), which is
|
|
|
+ called on window creation, and only when we know it's not a VK window. */
|
|
|
+ if (KMSDRM_DisplayDataInit(_this, dispdata)) {
|
|
|
+ ret = SDL_SetError("error getting KMS/DRM information");
|
|
|
+ goto cleanup;
|
|
|
}
|
|
|
|
|
|
- /* Setup the single display that's available */
|
|
|
-
|
|
|
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->crtc->buffer_id);
|
|
|
- display.desktop_mode.format = drmToSDLPixelFormat(fb->bpp, fb->depth);
|
|
|
- drmModeFreeFB(fb);
|
|
|
-#endif
|
|
|
-
|
|
|
display.current_mode = display.desktop_mode;
|
|
|
- display.driverdata = dispdata;
|
|
|
+
|
|
|
+ /* Add the display only when it's ready, */
|
|
|
SDL_AddVideoDisplay(&display, SDL_FALSE);
|
|
|
|
|
|
/* Use this if you ever need to see info on all available planes. */
|
|
@@ -1348,263 +1727,48 @@ KMSDRM_VideoInit(_THIS)
|
|
|
get_planes_info(_this);
|
|
|
#endif
|
|
|
|
|
|
- /* Setup display plane */
|
|
|
- ret = setup_plane(_this, &(dispdata->display_plane), DRM_PLANE_TYPE_PRIMARY);
|
|
|
- if (ret) {
|
|
|
- ret = SDL_SetError("can't find suitable display plane.");
|
|
|
- goto cleanup;
|
|
|
- }
|
|
|
-
|
|
|
- /* Get CRTC properties */
|
|
|
- dispdata->crtc->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
|
|
|
- dispdata->crtc->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 = SDL_OutOfMemory();
|
|
|
- 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,
|
|
|
- dispdata->connector->connector->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 = SDL_OutOfMemory();
|
|
|
- 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]);
|
|
|
- }
|
|
|
-
|
|
|
- /* Create aux dumb buffer. It's only useful to keep the PRIMARY PLANE occupied
|
|
|
- when we destroy the GBM surface and it's KMS buffers, so not being able to
|
|
|
- create it is not fatal. */
|
|
|
- dispdata->dumb_buffer = KMSDRM_CreateBuffer(_this);
|
|
|
- if (!dispdata->dumb_buffer) {
|
|
|
- ret = SDL_SetError("can't create dumb buffer.");
|
|
|
- } else {
|
|
|
- /* Fill the dumb buffer with black pixels. */
|
|
|
- KMSDRM_FillDumbBuffer(dispdata->dumb_buffer);
|
|
|
- }
|
|
|
-
|
|
|
- /*********************/
|
|
|
- /* Atomic block ends */
|
|
|
- /*********************/
|
|
|
-
|
|
|
#ifdef SDL_INPUT_LINUXEV
|
|
|
SDL_EVDEV_Init();
|
|
|
#endif
|
|
|
|
|
|
- KMSDRM_InitMouse(_this);
|
|
|
-
|
|
|
viddata->video_init = SDL_TRUE;
|
|
|
|
|
|
cleanup:
|
|
|
|
|
|
- if (encoder)
|
|
|
- KMSDRM_drmModeFreeEncoder(encoder);
|
|
|
- if (resources)
|
|
|
- KMSDRM_drmModeFreeResources(resources);
|
|
|
- if (ret != 0) {
|
|
|
- /* Error (complete) cleanup */
|
|
|
- if (dispdata->connector->connector) {
|
|
|
- KMSDRM_drmModeFreeConnector(dispdata->connector->connector);
|
|
|
- dispdata->connector = NULL;
|
|
|
- }
|
|
|
- if (dispdata->crtc->crtc) {
|
|
|
- KMSDRM_drmModeFreeCrtc(dispdata->crtc->crtc);
|
|
|
- dispdata->crtc = NULL;
|
|
|
- }
|
|
|
- if (viddata->gbm_dev) {
|
|
|
- KMSDRM_gbm_device_destroy(viddata->gbm_dev);
|
|
|
- viddata->gbm_dev = NULL;
|
|
|
- }
|
|
|
- if (viddata->drm_fd >= 0) {
|
|
|
- close(viddata->drm_fd);
|
|
|
- viddata->drm_fd = -1;
|
|
|
- }
|
|
|
- SDL_free(dispdata);
|
|
|
+ if (ret) {
|
|
|
+ if (dispdata->display_plane) {
|
|
|
+ SDL_free(dispdata->display_plane);
|
|
|
+ }
|
|
|
+ if (dispdata->crtc) {
|
|
|
+ SDL_free(dispdata->crtc);
|
|
|
+ }
|
|
|
+ if (dispdata->connector) {
|
|
|
+ SDL_free(dispdata->connector);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-/* The driverdata pointers, like dispdata, viddata, etc...
|
|
|
+/* The driverdata pointers, like dispdata, viddata, windata, etc...
|
|
|
are freed by SDL internals, so not our job. */
|
|
|
void
|
|
|
KMSDRM_VideoQuit(_THIS)
|
|
|
{
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
- SDL_DisplayData *dispdata;
|
|
|
-
|
|
|
- KMSDRM_PlaneInfo plane_info = {0};
|
|
|
- drmModeModeInfo mode;
|
|
|
-
|
|
|
- uint32_t blob_id;
|
|
|
-
|
|
|
- /* Video was not initialized properly, hence SDL internals
|
|
|
- called VideoQuit(). We will segault somewhere if we go further. */
|
|
|
- if (!viddata->video_init) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- /* Don't call any SDL_GetDisplay* function until we get sure that
|
|
|
- VideoInit() succeeded, because the SDL_GetDisplay* call would
|
|
|
- throw it's own SDL_SetError(), overwritting the error set in
|
|
|
- the failed VideoInit() call. */
|
|
|
- dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
|
|
-
|
|
|
- /*****************************************************************/
|
|
|
- /* */
|
|
|
- /* BLOCK to safely destroy the DUMB BUFFER. */
|
|
|
- /* */
|
|
|
- /* Since the program should already have called DestroyWindow() */
|
|
|
- /* on all the windows by now, there's no need to destroy the */
|
|
|
- /* GBM/EGL surfaces and buffers of the windows here: they have */
|
|
|
- /* already been destroyed, and the PRIMARY PLANE is using the */
|
|
|
- /* DUMB BUFFER. BUT the DUMB BUFFER we use to keep the PRIMARY */
|
|
|
- /* PLANE occupied when we do DestroySurfaces calls is going to */
|
|
|
- /* be destroyed one way or another when the program quits, so */
|
|
|
- /* to avoid the kernel disabling the CRTC when it detects the */
|
|
|
- /* deletion of a buffer that IS IN USE BY THE PRIMARY PLANE, */
|
|
|
- /* we do one of these: */
|
|
|
- /* */
|
|
|
- /* -In AMDGPU, where manually disabling the CRTC and */
|
|
|
- /* disconnecting the CONNECTOR from the CRTC is an */
|
|
|
- /* unrecoverable situation, so we point the PRIMARY PLANE to */
|
|
|
- /* the original TTY buffer (not guaranteed to be there for us!) */
|
|
|
- /* and then destroy the DUMB BUFFER). */
|
|
|
- /* */
|
|
|
- /* -In other drivers, we 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 destroy the DUMB BUFFER. */
|
|
|
- /* We can leave all like this if we are exiting the program: */
|
|
|
- /* FBCON or whatever will reconfigure things as it needs. */
|
|
|
- /* */
|
|
|
- /*****************************************************************/
|
|
|
-
|
|
|
- mode = dispdata->crtc->crtc->mode;
|
|
|
-
|
|
|
-#if AMDGPU_COMPAT
|
|
|
- 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 = mode.hdisplay;
|
|
|
- plane_info.src_h = mode.vdisplay;
|
|
|
- plane_info.crtc_w = mode.hdisplay;
|
|
|
- plane_info.crtc_h = mode.vdisplay;
|
|
|
-
|
|
|
- drm_atomic_set_plane_props(&plane_info);
|
|
|
-
|
|
|
-#else
|
|
|
-
|
|
|
- add_connector_property(dispdata->atomic_req, dispdata->connector , "CRTC_ID", 0);
|
|
|
- add_crtc_property(dispdata->atomic_req, dispdata->crtc , "ACTIVE", 0);
|
|
|
-
|
|
|
- /* Since we initialize plane_info to all zeros, ALL PRIMARY PLANE props are set to 0 with this,
|
|
|
- including FB_ID and CRTC_ID. Not all drivers like FB_ID and CRTC_ID to 0 yet. */
|
|
|
- plane_info.plane = dispdata->display_plane;
|
|
|
+ SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
|
|
|
|
|
- drm_atomic_set_plane_props(&plane_info);
|
|
|
+ KMSDRM_DisplayDataDeinit(_this, dispdata);
|
|
|
|
|
|
+#ifdef SDL_INPUT_LINUXEV
|
|
|
+ SDL_EVDEV_Quit();
|
|
|
#endif
|
|
|
|
|
|
- /* Set props that restore the original video mode. */
|
|
|
- dispdata->atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
|
|
- add_connector_property(dispdata->atomic_req, dispdata->connector, "CRTC_ID", dispdata->crtc->crtc->crtc_id);
|
|
|
- KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &mode, sizeof(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);
|
|
|
-
|
|
|
- /* Issue blocking atomic commit. */
|
|
|
- if (drm_atomic_commit(_this, SDL_TRUE)) {
|
|
|
- SDL_SetError("Failed to issue atomic commit on video quitting.");
|
|
|
- }
|
|
|
-
|
|
|
- /* Destroy the DUMB buffer if it exists, now that it's not being
|
|
|
- used anymore by the PRIMARY PLANE. */
|
|
|
- if (dispdata->dumb_buffer) {
|
|
|
- KMSDRM_DestroyDumbBuffer(_this, dispdata->dumb_buffer);
|
|
|
- }
|
|
|
-
|
|
|
- /***************/
|
|
|
- /* BLOCK ENDS. */
|
|
|
- /***************/
|
|
|
-
|
|
|
/* Clear out the window list */
|
|
|
SDL_free(viddata->windows);
|
|
|
viddata->windows = NULL;
|
|
|
viddata->max_windows = 0;
|
|
|
viddata->num_windows = 0;
|
|
|
-
|
|
|
-#if SDL_VIDEO_OPENGL_EGL
|
|
|
- if (_this->gl_config.driver_loaded) {
|
|
|
- SDL_GL_UnloadLibrary();
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
- /* Free connector */
|
|
|
- if (dispdata && dispdata->connector) {
|
|
|
- if (dispdata->connector->connector) {
|
|
|
- KMSDRM_drmModeFreeConnector(dispdata->connector->connector);
|
|
|
- dispdata->connector->connector = NULL;
|
|
|
- }
|
|
|
- if (dispdata->connector->props_info) {
|
|
|
- SDL_free(dispdata->connector->props_info);
|
|
|
- dispdata->connector->props_info = NULL;
|
|
|
- }
|
|
|
- SDL_free(dispdata->connector);
|
|
|
- dispdata->connector = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- /* Free CRTC */
|
|
|
- if (dispdata && dispdata->crtc) {
|
|
|
- if (dispdata->crtc->crtc) {
|
|
|
- KMSDRM_drmModeFreeCrtc(dispdata->crtc->crtc);
|
|
|
- dispdata->crtc->crtc = NULL;
|
|
|
- }
|
|
|
- if (dispdata->crtc->props_info) {
|
|
|
- SDL_free(dispdata->crtc->props_info);
|
|
|
- dispdata->crtc->props_info = NULL;
|
|
|
- }
|
|
|
- SDL_free(dispdata->crtc);
|
|
|
- dispdata->crtc = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- /* 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 by DestroyWindow() when we get here. */
|
|
|
- if (viddata->gbm_dev) {
|
|
|
- KMSDRM_gbm_device_destroy(viddata->gbm_dev);
|
|
|
- viddata->gbm_dev = NULL;
|
|
|
- }
|
|
|
- if (viddata->drm_fd >= 0) {
|
|
|
- close(viddata->drm_fd);
|
|
|
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Closed DRM FD %d", viddata->drm_fd);
|
|
|
- viddata->drm_fd = -1;
|
|
|
- }
|
|
|
-#ifdef SDL_INPUT_LINUXEV
|
|
|
- SDL_EVDEV_Quit();
|
|
|
-#endif
|
|
|
viddata->video_init = SDL_FALSE;
|
|
|
}
|
|
|
|
|
@@ -1690,19 +1854,36 @@ KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
|
|
|
int
|
|
|
KMSDRM_CreateWindow(_THIS, SDL_Window * window)
|
|
|
{
|
|
|
- SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
|
|
|
- SDL_VideoDisplay *display = NULL;
|
|
|
- SDL_DisplayData *dispdata = NULL;
|
|
|
SDL_WindowData *windata = NULL;
|
|
|
+ SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
|
|
|
+ SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
|
|
|
+ SDL_DisplayData *dispdata = display->driverdata;
|
|
|
+ SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
|
|
|
+ NativeDisplayType egl_display;
|
|
|
+
|
|
|
float ratio;
|
|
|
|
|
|
-#if SDL_VIDEO_OPENGL_EGL
|
|
|
- if (!_this->egl_data) {
|
|
|
- if (SDL_GL_LoadLibrary(NULL) < 0) {
|
|
|
- goto error;
|
|
|
- }
|
|
|
+ if ( !(dispdata->gbm_init) && (!is_vulkan)) {
|
|
|
+ /* Reopen FD, create gbm dev, setup display plane, etc,.
|
|
|
+ but only when we come here for the first time,
|
|
|
+ and only if it's not a VK window. */
|
|
|
+ KMSDRM_GBMInit(_this, dispdata);
|
|
|
+
|
|
|
+ /* Manually load the EGL library. KMSDRM_EGL_LoadLibrary() has already
|
|
|
+ been called by SDL_CreateWindow() but we don't do anything there,
|
|
|
+ precisely to be able to load it here.
|
|
|
+ If we let SDL_CreateWindow() load the lib, it will be loaded
|
|
|
+ before we call KMSDRM_GBMInit(), causing GLES programs to fail. */
|
|
|
+ // TODO errorcheck the return value of this.
|
|
|
+ if (!_this->egl_data) {
|
|
|
+ egl_display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm_dev;
|
|
|
+ SDL_EGL_LoadLibrary(_this, NULL, egl_display, EGL_PLATFORM_GBM_MESA);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Can't init mouse sooner because planes are not ready. */
|
|
|
+ // TODO Add a mouse_init bool and use it to avoid double intializations.
|
|
|
+ KMSDRM_InitMouse(_this);
|
|
|
}
|
|
|
-#endif
|
|
|
|
|
|
/* Allocate window internal data */
|
|
|
windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
|
|
@@ -1711,9 +1892,6 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window)
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
- display = SDL_GetDisplayForWindow(window);
|
|
|
- dispdata = display->driverdata;
|
|
|
-
|
|
|
if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
|
|
|
((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN))
|
|
|
{
|
|
@@ -1744,8 +1922,10 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window)
|
|
|
windata->viddata = viddata;
|
|
|
window->driverdata = windata;
|
|
|
|
|
|
- if (KMSDRM_CreateSurfaces(_this, window)) {
|
|
|
- goto error;
|
|
|
+ if (!is_vulkan) {
|
|
|
+ if (KMSDRM_CreateSurfaces(_this, window)) {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* Add window to the internal list of tracked windows. Note, while it may
|