kmsdrm: update SwapWindow fn, moving it to triple-buffer.
diff --git a/src/video/kmsdrm/SDL_kmsdrmopengles.c b/src/video/kmsdrm/SDL_kmsdrmopengles.c
index 34bcb8c..e4274a9 100644
--- a/src/video/kmsdrm/SDL_kmsdrmopengles.c
+++ b/src/video/kmsdrm/SDL_kmsdrmopengles.c
@@ -73,8 +73,21 @@
return fence;
}
+/*
+-We have to stop the GPU from executing the cmdstream again because the front butter
+ has been marked as back buffer so it can be selected by EGL to draw on it BUT
+ the pageflip has not completed, so it's still on screen, so letting GPU render on it
+ would cause tearing. (kms_fence)
+-We have to stop the DISPLAY from doing the changes we requested in the atomic ioctl,
+ like the pageflip, until the GPU has completed the cmdstream execution,
+ because we don't want the pageflip to be done in the middle of a frame rendering.
+ (gpu_fence).
+-We have to stop the program from doing a new atomic ioctl until the previous one
+has been finished. (kms_fence)
+*/
+
int
-KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
+KMSDRM_GLES_SwapWindowDOUBLE(_THIS, SDL_Window * window) {
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
@@ -85,13 +98,13 @@
uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK;
- EGLSyncKHR display_in_fence = NULL;
- EGLSyncKHR display_out_fence = NULL;
+ EGLSyncKHR kms_in_fence = NULL;
+ EGLSyncKHR kms_out_fence = NULL;
/* Create the display in-fence, and export it as a fence fd to pass into the kernel. */
- display_in_fence = create_fence(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this);
- assert(display_in_fence);
- dispdata->kms_in_fence_fd = _this->egl_data->eglDupNativeFenceFDANDROID(_this->egl_data->egl_display, display_in_fence);
+ kms_in_fence = create_fence(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this);
+ assert(kms_in_fence);
+ dispdata->kms_in_fence_fd = _this->egl_data->eglDupNativeFenceFDANDROID(_this->egl_data->egl_display, kms_in_fence);
/* Mark the back buffer as the next front buffer, and the current front buffer as elegible
by EGL as back buffer to draw into.
@@ -124,18 +137,18 @@
}
/* Get the display out fence, returned by the atomic ioctl. */
- display_out_fence = create_fence(dispdata->kms_out_fence_fd, _this);
- assert(display_out_fence);
+ kms_out_fence = create_fence(dispdata->kms_out_fence_fd, _this);
+ assert(kms_out_fence);
/* Wait on the CPU side for the _previous_ commit to complete before we post the flip through KMS,
* because atomic will reject the commit if we post a new one while the previous one is still pending. */
do {
- status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display, display_out_fence, 0, EGL_FOREVER_KHR);
+ status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display, kms_out_fence, 0, EGL_FOREVER_KHR);
} while (status != EGL_CONDITION_SATISFIED_KHR);
/* Destroy both in and out display fences. */
- _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, display_out_fence);
- _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, display_in_fence);
+ _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, kms_out_fence);
+ _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, kms_in_fence);
/* Now that the pageflip is complete, release last front buffer so EGL can chose it
* as back buffer and render on it again: */
@@ -150,6 +163,178 @@
return ret;
}
+
+int
+KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
+
+ SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
+ SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
+ KMSDRM_FBInfo *fb;
+ int ret;
+
+ uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK;
+
+ /* Create the fence that will be inserted in the cmdstream exactly at the end
+ of the gl commands that form a frame. KMS will have to wait on it before doing a pageflip. */
+ dispdata->gpu_fence = create_fence(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this);
+ assert(dispdata->gpu_fence);
+
+ _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface);
+
+ /* It's safe to get the gpu_fence FD now, because eglSwapBuffers flushes it
+ down the cmdstream, so it's now in place in the cmdstream.
+ Atomic ioctl will pass the in-fence fd into the kernel. */
+ dispdata->kms_in_fence_fd = _this->egl_data->eglDupNativeFenceFDANDROID(_this->egl_data->egl_display, dispdata->gpu_fence);
+ _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->gpu_fence);
+ assert(dispdata->kms_in_fence_fd != -1);
+
+ /* Lock the buffer that is marked by eglSwapBuffers() to become the next front buffer (so it can not
+ be chosen by EGL as back buffer to draw on), and get a handle to it to request the pageflip on it. */
+ windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
+ if (!windata->next_bo) {
+ printf("Failed to lock frontbuffer\n");
+ return -1;
+ }
+ fb = KMSDRM_FBFromBO(_this, windata->next_bo);
+ if (!fb) {
+ printf("Failed to get a new framebuffer BO\n");
+ return -1;
+ }
+
+
+ /* Don't issue another atomic ioctl until previous one has completed. */
+ if (dispdata->kms_fence) {
+ EGLint status;
+
+ do {
+ status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0, EGL_FOREVER_KHR);
+ } while (status != EGL_CONDITION_SATISFIED_KHR);
+
+ _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->kms_fence);
+ }
+
+
+ /* Issue atomic commit. */
+ ret = drm_atomic_commit(_this, fb->fb_id, flags);
+ if (ret) {
+ printf("failed to do atomic commit\n");
+ return -1;
+ }
+
+
+
+ /* release last front buffer so EGL can chose it as back buffer and render on it again: */
+ if (windata->curr_bo) {
+ KMSDRM_gbm_surface_release_buffer(windata->gs, windata->curr_bo);
+ windata->curr_bo = NULL;
+ }
+
+ /* Take note of the current front buffer, so it can be freed next time this function is called. */
+ windata->curr_bo = windata->next_bo;
+
+ /* Import out fence from the out fence fd and tell the GPU to wait on it
+ until the requested pageflip has completed. */
+ dispdata->kms_fence = create_fence(dispdata->kms_out_fence_fd, _this);
+ assert(dispdata->kms_fence);
+
+ dispdata->kms_out_fence_fd = -1;
+
+ _this->egl_data->eglWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0);
+
+ return ret;
+
+}
+
+int
+KMSDRM_GLES_SwapWindowOLD(_THIS, SDL_Window * window) {
+
+ SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
+ SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
+ KMSDRM_FBInfo *fb;
+ int ret;
+
+ uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK;
+
+ dispdata->kms_fence = NULL; /* in-fence to gpu, out-fence from kms */
+ dispdata->gpu_fence = NULL; /* in-fence to kms, out-fence from gpu, */
+
+ /* Allow modeset (which is done inside atomic_commit). */
+ flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
+
+
+ /* Import out fence from the out fence fd and tell the GPU to wait on it
+ until the requested pageflip has completed. */
+ dispdata->kms_fence = create_fence(dispdata->kms_out_fence_fd, _this);
+ assert(dispdata->kms_fence);
+
+ dispdata->kms_out_fence_fd = -1;
+
+
+ _this->egl_data->eglWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0);
+
+
+ /* GL DRAW */
+
+ /* Create the gpu fence here so it's inserted in the cmdstream exactly
+ at the end of the gl commands that form a frame. */
+ dispdata->gpu_fence = create_fence(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this);
+ assert(dispdata->gpu_fence);
+
+ _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface);
+
+ /* It's safe to get the gpu_fence fd now, because eglSwapBuffers() flushes the fd. */
+ dispdata->kms_in_fence_fd = _this->egl_data->eglDupNativeFenceFDANDROID(_this->egl_data->egl_display, dispdata->gpu_fence);
+
+ _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->gpu_fence);
+ assert(dispdata->kms_in_fence_fd != -1);
+
+ windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
+ if (!windata->next_bo) {
+ printf("Failed to lock frontbuffer\n");
+ return -1;
+ }
+ fb = KMSDRM_FBFromBO(_this, windata->next_bo);
+ if (!fb) {
+ printf("Failed to get a new framebuffer BO\n");
+ return -1;
+ }
+
+ if (dispdata->kms_fence) {
+ EGLint status;
+
+ do {
+ status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0, EGL_FOREVER_KHR);
+ } while (status != EGL_CONDITION_SATISFIED_KHR);
+
+ _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->kms_fence);
+ }
+
+
+ /* Issue atomic commit. */
+ ret = drm_atomic_commit(_this, fb->fb_id, flags);
+ if (ret) {
+ printf("failed to do atomic commit\n");
+ return -1;
+ }
+
+
+
+ /* release last front buffer so EGL can chose it as back buffer and render on it again: */
+ if (windata->curr_bo) {
+ KMSDRM_gbm_surface_release_buffer(windata->gs, windata->curr_bo);
+ windata->curr_bo = NULL;
+ }
+
+ /* Take note of the current front buffer, so it can be freed next time this function is called. */
+ windata->curr_bo = windata->next_bo;
+
+ /* Allow a modeset change for the first commit only. */
+ flags &= ~(DRM_MODE_ATOMIC_ALLOW_MODESET);
+
+
+ return ret;
+}
+
/***************************************/
/* End of Atomic functions block */
/***************************************/
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c
index 417398a..fa2772b 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.c
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c
@@ -874,8 +874,11 @@
}
}
- /* Initialize the fence fd: */
+ /* Initialize the fences and their fds: */
+ dispdata->kms_fence = NULL;
+ dispdata->gpu_fence = NULL;
dispdata->kms_out_fence_fd = -1,
+ dispdata->kms_in_fence_fd = -1,
/*********************/
/* Atomic block ends */
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.h b/src/video/kmsdrm/SDL_kmsdrmvideo.h
index 897451b..e69fe17 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.h
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.h
@@ -34,6 +34,7 @@
#include <assert.h>
#if SDL_VIDEO_OPENGL_EGL
#include <EGL/egl.h>
+#include <EGL/eglext.h>
#endif
typedef struct SDL_VideoData
@@ -75,9 +76,13 @@
drmModePropertyRes **connector_props_info;
int crtc_index;
+
int kms_in_fence_fd;
int kms_out_fence_fd;
+ EGLSyncKHR kms_fence; /* Signaled when kms completes changes requested in atomic iotcl (pageflip, etc). */
+ EGLSyncKHR gpu_fence; /* Signaled when GPU rendering is done. */
+
} SDL_DisplayData;