st/egl: Implement swapbuffer throttling
authorThomas Hellstrom <thellstrom@vmware.com>
Fri, 25 Feb 2011 12:05:31 +0000 (13:05 +0100)
committerThomas Hellstrom <thellstrom@vmware.com>
Tue, 1 Mar 2011 09:36:19 +0000 (10:36 +0100)
When doing copy swapbuffers using drm, throttle on outstanding copy operations.
Introduces a new environment variable, EGL_THROTTLE_FENCES that the
user can use to indicate the desired number of outstanding swapbuffers, or
disable throttling using EGL_THROTTLE_FENCES=0.

This can and perhaps should be extended to the pageflip case as well, since
with some hardware pageflips can be pipelined. In case the pageflip syncs, the
throttle operation will be a no-op anyway.

Update copyright notices.

Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
src/gallium/state_trackers/egl/common/native_helper.c
src/gallium/state_trackers/egl/common/native_helper.h
src/gallium/state_trackers/egl/drm/modeset.c

index 0f00c4d13ef315ddca1188d3f6b45afab3082b6a..be6713d03a853a28d2d70077cbf722048eca8c88 100644 (file)
@@ -3,6 +3,7 @@
  * Version:  7.9
  *
  * Copyright (C) 2010 LunarG Inc.
+ * Copyright (C) 2011 VMware Inc. All rights reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -24,6 +25,7 @@
  *
  * Authors:
  *    Chia-I Wu <olv@lunarg.com>
+ *    Thomas Hellstrom <thellstrom@vmware.com>
  */
 
 #include "util/u_inlines.h"
 
 #include "native_helper.h"
 
+/**
+ * Number of swap fences and mask
+ */
+
+#define EGL_SWAP_FENCES_MAX 4
+#define EGL_SWAP_FENCES_MASK 3
+#define EGL_SWAP_FENCES_DEFAULT 1
+
 struct resource_surface {
    struct pipe_screen *screen;
    enum pipe_format format;
@@ -42,6 +52,15 @@ struct resource_surface {
    struct pipe_resource *resources[NUM_NATIVE_ATTACHMENTS];
    uint resource_mask;
    uint width, height;
+
+   /**
+    * Swap fences.
+    */
+   struct pipe_fence_handle *swap_fences[EGL_SWAP_FENCES_MAX];
+   unsigned int cur_fences;
+   unsigned int head;
+   unsigned int tail;
+   unsigned int desired_fences;
 };
 
 struct resource_surface *
@@ -49,11 +68,16 @@ resource_surface_create(struct pipe_screen *screen,
                         enum pipe_format format, uint bind)
 {
    struct resource_surface *rsurf = CALLOC_STRUCT(resource_surface);
+   char *swap_fences = getenv("EGL_THROTTLE_FENCES");
 
    if (rsurf) {
       rsurf->screen = screen;
       rsurf->format = format;
       rsurf->bind = bind;
+      rsurf->desired_fences = (swap_fences) ? atoi(swap_fences) :
+        EGL_SWAP_FENCES_DEFAULT;
+      if (rsurf->desired_fences > EGL_SWAP_FENCES_MAX)
+        rsurf->desired_fences = EGL_SWAP_FENCES_MAX;
    }
 
    return rsurf;
@@ -256,7 +280,6 @@ resource_surface_copy_swap(struct resource_surface *rsurf,
    u_box_origin_2d(ftex->width0, ftex->height0, &src_box);
    pipe->resource_copy_region(pipe, ftex, 0, 0, 0, 0,
                              btex, 0, &src_box);
-   pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE, NULL);
    ret = TRUE;
 
  out_no_ftex:
@@ -266,3 +289,81 @@ resource_surface_copy_swap(struct resource_surface *rsurf,
 
    return ret;
 }
+
+static struct pipe_fence_handle *
+swap_fences_pop_front(struct resource_surface *rsurf)
+{
+   struct pipe_screen *screen = rsurf->screen;
+   struct pipe_fence_handle *fence = NULL;
+
+   if (rsurf->desired_fences == 0)
+      return NULL;
+
+   if (rsurf->cur_fences >= rsurf->desired_fences) {
+      screen->fence_reference(screen, &fence, rsurf->swap_fences[rsurf->tail]);
+      screen->fence_reference(screen, &rsurf->swap_fences[rsurf->tail++], NULL);
+      rsurf->tail &= EGL_SWAP_FENCES_MASK;
+      --rsurf->cur_fences;
+   }
+   return fence;
+}
+
+static void
+swap_fences_push_back(struct resource_surface *rsurf,
+                     struct pipe_fence_handle *fence)
+{
+   struct pipe_screen *screen = rsurf->screen;
+
+   if (!fence || rsurf->desired_fences == 0)
+      return;
+
+   while(rsurf->cur_fences == rsurf->desired_fences)
+      swap_fences_pop_front(rsurf);
+
+   rsurf->cur_fences++;
+   screen->fence_reference(screen, &rsurf->swap_fences[rsurf->head++],
+                          fence);
+   rsurf->head &= EGL_SWAP_FENCES_MASK;
+}
+
+boolean
+resource_surface_throttle(struct resource_surface *rsurf)
+{
+   struct pipe_screen *screen = rsurf->screen;
+   struct pipe_fence_handle *fence = swap_fences_pop_front(rsurf);
+
+   if (fence) {
+      (void) screen->fence_finish(screen, fence, 0);
+      screen->fence_reference(screen, &fence, NULL);
+      return TRUE;
+   }
+
+   return FALSE;
+}
+
+boolean
+resource_surface_flush(struct resource_surface *rsurf,
+                      struct native_display *ndpy)
+{
+   struct pipe_fence_handle *fence = NULL;
+   struct pipe_screen *screen = rsurf->screen;
+   struct pipe_context *pipe= ndpy_get_copy_context(ndpy);
+
+   if (!pipe)
+      return FALSE;
+
+   pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE, &fence);
+   if (fence == NULL)
+      return FALSE;
+
+   swap_fences_push_back(rsurf, fence);
+   screen->fence_reference(screen, &fence, NULL);
+
+   return TRUE;
+}
+
+void
+resource_surface_wait(struct resource_surface *rsurf)
+{
+   while (resource_surface_throttle(rsurf));
+}
index ad6827336a3dd9311cbb4be0877afaf723dc8e9f..39564a043655827370fee33f3d152b25f8b17597 100644 (file)
@@ -3,6 +3,7 @@
  * Version:  7.9
  *
  * Copyright (C) 2010 LunarG Inc.
+ * Copyright (C) 2011 VMware Inc. All rights reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -24,6 +25,7 @@
  *
  * Authors:
  *    Chia-I Wu <olv@lunarg.com>
+ *    Thomas Hellstrom <thellstrom@vmware.com>
  */
 
 #include "native.h"
@@ -75,6 +77,31 @@ resource_surface_present(struct resource_surface *rsurf,
                          enum native_attachment which,
                          void *winsys_drawable_handle);
 
+/**
+ * Perform a gallium copy blit between the back left and front left
+ * surfaces. Needs to be followed by a call to resource_surface_flush.
+ */
 boolean
 resource_surface_copy_swap(struct resource_surface *rsurf,
                           struct native_display *ndpy);
+
+/**
+ * Throttle on outstanding rendering using the copy context. For example
+ * copy swaps.
+ */
+boolean
+resource_surface_throttle(struct resource_surface *rsurf);
+
+/**
+ * Flush pending rendering using the copy context. This function saves a
+ * marker for upcoming throttles.
+ */
+boolean
+resource_surface_flush(struct resource_surface *rsurf,
+                      struct native_display *ndpy);
+/**
+ * Wait for all rendering using the copy context to be complete. Frees all
+ * throttle markers saved using resource_surface_flush.
+ */
+void
+resource_surface_wait(struct resource_surface *rsurf);
index 6eaa42fafb1d129727c241f9474e57fca39e2f41..3fff9540905d479e430d0773797a8b83928e333f 100644 (file)
@@ -3,6 +3,7 @@
  * Version:  7.9
  *
  * Copyright (C) 2010 LunarG Inc.
+ * Copyright (C) 2011 VMware Inc. All rights reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -24,6 +25,7 @@
  *
  * Authors:
  *    Chia-I Wu <olv@lunarg.com>
+ *    Thomas Hellstrom <thellstrom@vmware.com>
  */
 
 #include "util/u_memory.h"
@@ -136,8 +138,12 @@ drm_surface_copy_swap(struct native_surface *nsurf)
    struct drm_surface *drmsurf = drm_surface(nsurf);
    struct drm_display *drmdpy = drmsurf->drmdpy;
 
-   if (!resource_surface_copy_swap(drmsurf->rsurf, &drmdpy->base) ||
-       !drm_surface_flush_frontbuffer(nsurf))
+   (void) resource_surface_throttle(drmsurf->rsurf);
+   if (!resource_surface_copy_swap(drmsurf->rsurf, &drmdpy->base))
+      return FALSE;
+
+   (void) resource_surface_flush(drmsurf->rsurf, &drmdpy->base);
+   if (!drm_surface_flush_frontbuffer(nsurf))
       return FALSE;
 
    drmsurf->sequence_number++;
@@ -218,7 +224,9 @@ drm_surface_present(struct native_surface *nsurf,
 static void
 drm_surface_wait(struct native_surface *nsurf)
 {
-   /* no-op */
+   struct drm_surface *drmsurf = drm_surface(nsurf);
+
+   resource_surface_wait(drmsurf->rsurf);
 }
 
 static void
@@ -226,6 +234,7 @@ drm_surface_destroy(struct native_surface *nsurf)
 {
    struct drm_surface *drmsurf = drm_surface(nsurf);
 
+   resource_surface_wait(drmsurf->rsurf);
    if (drmsurf->current_crtc.crtc)
          drmModeFreeCrtc(drmsurf->current_crtc.crtc);