kmsdrm: rewrite the new SwapWindow() fn to avoid tearing. Double-buffer only for now.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
diff --git a/src/video/kmsdrm/SDL_kmsdrmopengles.c b/src/video/kmsdrm/SDL_kmsdrmopengles.c
index b83ffb7..34bcb8c 100644
--- a/src/video/kmsdrm/SDL_kmsdrmopengles.c
+++ b/src/video/kmsdrm/SDL_kmsdrmopengles.c
@@ -79,44 +79,29 @@ 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;
+ EGLint status;
+ int ret;
+
uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK;
- EGLSyncKHR gpu_fence = NULL; /* out-fence from gpu, in-fence to kms */
- EGLSyncKHR kms_fence = NULL; /* in-fence to gpu, out-fence from kms */
-
- /* Allow modeset (which is done inside atomic_commit). */
- flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
-
- if (dispdata->kms_out_fence_fd != -1) {
- kms_fence = create_fence(dispdata->kms_out_fence_fd, _this);
- assert(kms_fence);
-
- /* driver now has ownership of the fence fd: */
- dispdata->kms_out_fence_fd = -1;
-
- /* wait "on the gpu" (ie. this won't necessarily block, but
- * will block the rendering until fence is signaled), until
- * the previous pageflip completes so we don't render into
- * the buffer that is still on screen.
- */
- _this->egl_data->eglWaitSyncKHR(_this->egl_data->egl_display, kms_fence, 0);
- }
-
- /* insert fence to be singled in cmdstream.. this fence will be
- * signaled when gpu rendering done
- */
- gpu_fence = create_fence(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this);
- assert(gpu_fence);
-
+ EGLSyncKHR display_in_fence = NULL;
+ EGLSyncKHR display_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);
+
+ /* Mark the back buffer as the next front buffer, and the current front buffer as elegible
+ by EGL as back buffer to draw into.
+ This will not really happen until we post the pageflip though KMS with the atomic ioctl
+ and it completes on next vsync. */
_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface);
- /* after swapbuffers, gpu_fence should be flushed, so safe to get the fd: */
- dispdata->kms_in_fence_fd = _this->egl_data->eglDupNativeFenceFDANDROID(_this->egl_data->egl_display, gpu_fence);
- _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, 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, so we can use that handle
+ to request the KMS pageflip that will set it as front buffer. */
windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
if (!windata->next_bo) {
printf("Failed to lock frontbuffer\n");
@@ -128,27 +113,9 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
return -1;
}
- if (kms_fence) {
- EGLint status;
-
- /* Wait on the CPU side for the _previous_ commit to
- * complete before we post the flip through KMS, as
- * atomic will reject the commit if we post a new one
- * whilst the previous one is still pending.
- */
- do {
- status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display,
- kms_fence,
- 0,
- EGL_FOREVER_KHR);
- } while (status != EGL_CONDITION_SATISFIED_KHR);
-
- _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, kms_fence);
- }
-
- /*
- * Here you could also update drm plane layers if you want
- * hw composition
+ /*
+ * Issue atomic pageflip to post the pageflip thru KMS. Returns immediately.
+ * Never issue an atomic ioctl while the previuos one is pending: it will be rejected.
*/
ret = drm_atomic_commit(_this, fb->fb_id, flags);
if (ret) {
@@ -156,7 +123,22 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
return -1;
}
- /* release last buffer to render on again: */
+ /* 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);
+
+ /* 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);
+ } 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);
+
+ /* Now that the pageflip is complete, 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;
@@ -165,9 +147,6 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
/* 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;
}