st/mesa: implement a simple cache for glDrawPixels
authorBrian Paul <brianp@vmware.com>
Fri, 19 Feb 2016 15:51:51 +0000 (08:51 -0700)
committerBrian Paul <brianp@vmware.com>
Fri, 19 Feb 2016 15:51:51 +0000 (08:51 -0700)
Instead of discarding the texture we created, keep it around in case
the next glDrawPixels draws the same image again.  This is intended
to help application which draw the same image several times in a row,
either within a frame or subsequent frames.

Reviewed-by: Charmaine Lee <charmainel@vmware.com>
src/mesa/state_tracker/st_cb_drawpixels.c
src/mesa/state_tracker/st_context.c
src/mesa/state_tracker/st_context.h

index d1fe3302c605237b1c21cd8c7228eb6da3a18bc3..51d4ae51918ea756e0e2d739e6d7c2dd9a361e1a 100644 (file)
 #include "cso_cache/cso_context.h"
 
 
+/**
+ * We have a simple glDrawPixels cache to try to optimize the case where the
+ * same image is drawn over and over again.  It basically works as follows:
+ *
+ * 1. After we construct a texture map with the image and draw it, we do
+ *    not discard the texture.  We keep it around, plus we note the
+ *    glDrawPixels width, height, format, etc. parameters and keep a copy
+ *    of the image in a malloc'd buffer.
+ *
+ * 2. On the next glDrawPixels we check if the parameters match the previous
+ *    call.  If those match, we check if the image matches the previous image
+ *    via a memcmp() call.  If everything matches, we re-use the previous
+ *    texture, thereby avoiding the cost creating a new texture and copying
+ *    the image to it.
+ *
+ * The effectiveness of this cache depends upon:
+ * 1. If the memcmp() finds a difference, it happens relatively quickly.
+      Hopefully, not just the last pixels differ!
+ * 2. If the memcmp() finds no difference, doing that check is faster than
+ *    creating and loading a texture.
+ *
+ * Notes:
+ * 1. We don't support any pixel unpacking parameters.
+ * 2. We don't try to cache images in Pixel Buffer Objects.
+ * 3. Instead of saving the whole image, perhaps some sort of reliable
+ *    checksum function could be used instead.
+ */
+#define USE_DRAWPIXELS_CACHE 1
+
+
+
 /**
  * Create fragment program that does a TEX() instruction to get a Z and/or
  * stencil value value, then writes to FRAG_RESULT_DEPTH/FRAG_RESULT_STENCIL.
@@ -347,6 +378,39 @@ make_texture(struct st_context *st,
    enum pipe_format pipeFormat;
    GLenum baseInternalFormat;
 
+#if USE_DRAWPIXELS_CACHE
+   const GLint bpp = _mesa_bytes_per_pixel(format, type);
+
+   /* Check if the glDrawPixels() parameters and state matches the cache */
+   if (width == st->drawpix_cache.width &&
+       height == st->drawpix_cache.height &&
+       format == st->drawpix_cache.format &&
+       type == st->drawpix_cache.type &&
+       pixels == st->drawpix_cache.user_pointer &&
+       !_mesa_is_bufferobj(unpack->BufferObj) &&
+       (unpack->RowLength == 0 || unpack->RowLength == width) &&
+       unpack->SkipPixels == 0 &&
+       unpack->SkipRows == 0 &&
+       unpack->SwapBytes == GL_FALSE &&
+       st->drawpix_cache.image) {
+      /* check if the pixel data is the same */
+      if (memcmp(pixels, st->drawpix_cache.image, width * height * bpp) == 0) {
+         /* OK, re-use the cached texture */
+         return st->drawpix_cache.texture;
+      }
+   }
+
+   /* discard the cached image and texture (if there is one) */
+   st->drawpix_cache.width = 0;
+   st->drawpix_cache.height = 0;
+   st->drawpix_cache.user_pointer = NULL;
+   if (st->drawpix_cache.image) {
+      free(st->drawpix_cache.image);
+      st->drawpix_cache.image = NULL;
+   }
+   pipe_resource_reference(&st->drawpix_cache.texture, NULL);
+#endif
+
    /* Choose a pixel format for the temp texture which will hold the
     * image to draw.
     */
@@ -437,6 +501,25 @@ make_texture(struct st_context *st,
 
    _mesa_unmap_pbo_source(ctx, unpack);
 
+#if USE_DRAWPIXELS_CACHE
+   /* Save the glDrawPixels parameter and image in the cache */
+   if ((unpack->RowLength == 0 || unpack->RowLength == width) &&
+       unpack->SkipPixels == 0 &&
+       unpack->SkipRows == 0) {
+      st->drawpix_cache.width = width;
+      st->drawpix_cache.height = height;
+      st->drawpix_cache.format = format;
+      st->drawpix_cache.type = type;
+      st->drawpix_cache.user_pointer = pixels;
+      assert(!st->drawpix_cache.image);
+      st->drawpix_cache.image = malloc(width * height * bpp);
+      if (st->drawpix_cache.image) {
+         memcpy(st->drawpix_cache.image, pixels, width * height * bpp);
+      }
+      st->drawpix_cache.texture = pt;
+   }
+#endif
+
    return pt;
 }
 
@@ -1067,7 +1150,9 @@ st_DrawPixels(struct gl_context *ctx, GLint x, GLint y,
    if (num_sampler_view > 1)
       pipe_sampler_view_reference(&sv[1], NULL);
 
+#if !USE_DRAWPIXELS_CACHE
    pipe_resource_reference(&pt, NULL);
+#endif
 }
 
 
index c35055a0a97636c10701974feda778c7b0a09ada..e3ddee660f747d59bb40b0fb86a67e4d82144c60 100644 (file)
@@ -186,6 +186,10 @@ st_destroy_context_priv(struct st_context *st)
       u_upload_destroy(st->constbuf_uploader);
    }
 
+   /* free glDrawPixels cache data */
+   free(st->drawpix_cache.image);
+   pipe_resource_reference(&st->drawpix_cache.texture, NULL);
+
    cso_destroy_context(st->cso_context);
    free( st );
 }
index 1701c618ebff86367cd676b123e8f317e08a2cfd..f960c64cbe8dd1a1d3d04a1b98b931a3e912125c 100644 (file)
@@ -217,6 +217,14 @@ struct st_context
       void *vert_shaders[2];   /**< ureg shaders */
    } drawpix;
 
+   struct {
+      GLsizei width, height;
+      GLenum format, type;
+      const void *user_pointer;  /**< Last user 'pixels' pointer */
+      void *image;               /**< Copy of the glDrawPixels image data */
+      struct pipe_resource *texture;
+   } drawpix_cache;
+
    /** for glClear */
    struct {
       struct pipe_rasterizer_state raster;