浏览代码

kmsdrm: update SwapWindow fn, moving it to triple-buffer.

Manuel Alfayate Corchete 5 年之前
父节点
当前提交
8996ee1786
共有 3 个文件被更改,包括 205 次插入12 次删除
  1. 196 11
      src/video/kmsdrm/SDL_kmsdrmopengles.c
  2. 4 1
      src/video/kmsdrm/SDL_kmsdrmvideo.c
  3. 5 0
      src/video/kmsdrm/SDL_kmsdrmvideo.h

+ 196 - 11
src/video/kmsdrm/SDL_kmsdrmopengles.c

@@ -73,8 +73,21 @@ static EGLSyncKHR create_fence(int fd, _THIS)
 	return fence;
 	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
 int
-KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
+KMSDRM_GLES_SwapWindowDOUBLE(_THIS, SDL_Window * window) {
 
 
     SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
     SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
     SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
     SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
@@ -85,13 +98,13 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
     
     
     uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK;
     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. */
     /* 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
     /* Mark the back buffer as the next front buffer, and the current front buffer as elegible
        by EGL as back buffer to draw into.
        by EGL as back buffer to draw into.
@@ -124,18 +137,18 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
     }
     }
 
 
     /* Get the display out fence, returned by the atomic ioctl. */
     /* 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,
     /* 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. */
      * because atomic will reject the commit if we post a new one while the previous one is still pending. */
     do {
     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);
     } while (status != EGL_CONDITION_SATISFIED_KHR);
 
 
     /* Destroy both in and out display fences. */
     /* 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
     /* Now that the pageflip is complete, release last front buffer so EGL can chose it
      * as back buffer and render on it again: */
      * as back buffer and render on it again: */
@@ -150,6 +163,178 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
     return ret;
     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       */
 /* End of Atomic functions block       */
 /***************************************/
 /***************************************/

+ 4 - 1
src/video/kmsdrm/SDL_kmsdrmvideo.c

@@ -874,8 +874,11 @@ KMSDRM_VideoInit(_THIS)
 	}
 	}
     }
     }
 
 
-    /* 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_out_fence_fd = -1,
+    dispdata->kms_in_fence_fd  = -1,
 
 
     /*********************/
     /*********************/
     /* Atomic block ends */
     /* Atomic block ends */

+ 5 - 0
src/video/kmsdrm/SDL_kmsdrmvideo.h

@@ -34,6 +34,7 @@
 #include <assert.h>
 #include <assert.h>
 #if SDL_VIDEO_OPENGL_EGL
 #if SDL_VIDEO_OPENGL_EGL
 #include <EGL/egl.h>
 #include <EGL/egl.h>
+#include <EGL/eglext.h>
 #endif
 #endif
 
 
 typedef struct SDL_VideoData
 typedef struct SDL_VideoData
@@ -75,9 +76,13 @@ typedef struct SDL_DisplayData
     drmModePropertyRes **connector_props_info;
     drmModePropertyRes **connector_props_info;
 
 
     int crtc_index;
     int crtc_index;
+
     int kms_in_fence_fd;
     int kms_in_fence_fd;
     int kms_out_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;
 } SDL_DisplayData;