On 11 September 2012 12:04, Chad Versace <span dir="ltr"><<a href="mailto:chad.versace@linux.intel.com" target="_blank">chad.versace@linux.intel.com</a>></span> wrote:<br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
This patch adds a new fast path for glTexImage2D and glTexSubImage2D,<br>
intel_texsubimage_tiled_memcpy, which is optimized for Google Chrome's<br>
paint rectangles. The fast path is implemented only for 2D GL_BGRA<br>
textures on gen >= 6.<br>
<br>
Reduces the time spent in glTexImage and glTexSubImage by roughly 3x on<br>
Sandybridge for the workload described below.<br>
<br>
=== Performance Analysis ===<br>
<br>
Workload description:<br>
<br>
    Personalize your <a href="http://google.com" target="_blank">google.com</a> page with a wallpaper.  Start chromium<br>
with flags "--ignore-gpu-blacklist --enable-accelerated-painting<br>
--force-compositing-mode".  Start recording with chrome://tracing. Visit<br>
<a href="http://google.com" target="_blank">google.com</a> and wait for page to finish rendering.  Measure the time spent<br>
by process CrGpuMain in GLES2DecoderImpl::HandleTexImage2D and<br>
HandleTexSubImage2D.<br>
<br>
System config:<br>
<br>
    cpu: Sandybridge Mobile GT2+ (0x0126)<br>
    kernel 3.4.9 x86_64<br>
    chromium 21.0.1180.81 (151980)<br>
<br>
Statistics:<br>
<br>
           |  N   Median  Avg   Stddev<br>
    -------|--------------------------<br>
    before | 10   184.6  187.4  15.9<br>
    after  | 10   564.2  558.8  24.8<br>
<br>
    Difference at 95.0% confidence:<br>
        371.373 +/- 19.533<br>
        198.192% +/- 10.4243%<br>
<br>
    Ratio:<br>
        avg:    3.06<br>
        median: 2.99<br>
<br>
CC: Stéphane Marchesin <<a href="mailto:marcheu@chromium.org">marcheu@chromium.org</a>><br>
Signed-off-by: Chad Versace <<a href="mailto:chad.versace@linux.intel.com">chad.versace@linux.intel.com</a>><br>
---<br>
<br>
Stephane,<br>
<br>
This patch applies cleanly to master. Since the patch now occurs at<br>
a significantly different point in history compared to the other patch I sent<br>
you, I reran the workloads and updated the scores.<br>
<br>
<br>
 src/mesa/drivers/dri/intel/intel_tex.h          |  11 ++<br>
 src/mesa/drivers/dri/intel/intel_tex_image.c    |  12 ++<br>
 src/mesa/drivers/dri/intel/intel_tex_subimage.c | 170 ++++++++++++++++++++++++<br>
 3 files changed, 193 insertions(+)<br>
<br>
diff --git a/src/mesa/drivers/dri/intel/intel_tex.h b/src/mesa/drivers/dri/intel/intel_tex.h<br>
index 88a7d55..777574d 100644<br>
--- a/src/mesa/drivers/dri/intel/intel_tex.h<br>
+++ b/src/mesa/drivers/dri/intel/intel_tex.h<br>
@@ -85,4 +85,15 @@ bool intel_copy_texsubimage(struct intel_context *intel,<br>
                             GLint x, GLint y,<br>
                             GLsizei width, GLsizei height);<br>
<br>
+bool<br>
+intel_texsubimage_tiled_memcpy(struct gl_context *ctx,<br>
+                               GLuint dims,<br>
+                               struct gl_texture_image *texImage,<br>
+                               GLint xoffset, GLint yoffset, GLint zoffset,<br>
+                               GLsizei width, GLsizei height, GLsizei depth,<br>
+                               GLenum format, GLenum type,<br>
+                               const GLvoid *pixels,<br>
+                               const struct gl_pixelstore_attrib *packing,<br>
+                               bool for_glTexImage);<br>
+<br>
 #endif<br>
diff --git a/src/mesa/drivers/dri/intel/intel_tex_image.c b/src/mesa/drivers/dri/intel/intel_tex_image.c<br>
index fe9040c..bbb2fe0 100644<br>
--- a/src/mesa/drivers/dri/intel/intel_tex_image.c<br>
+++ b/src/mesa/drivers/dri/intel/intel_tex_image.c<br>
@@ -206,10 +206,22 @@ intelTexImage(struct gl_context * ctx,<br>
               GLenum format, GLenum type, const void *pixels,<br>
               const struct gl_pixelstore_attrib *unpack)<br>
 {<br>
+   bool ok;<br>
+<br>
    DBG("%s target %s level %d %dx%dx%d\n", __FUNCTION__,<br>
        _mesa_lookup_enum_by_nr(texImage->TexObject->Target),<br>
        texImage->Level, texImage->Width, texImage->Height, texImage->Depth);<br>
<br>
+   ok = intel_texsubimage_tiled_memcpy(ctx, dims, texImage,<br>
+                                       0, 0, 0, /*x,y,z offsets*/<br>
+                                       texImage->Width,<br>
+                                       texImage->Height,<br>
+                                       texImage->Depth,<br>
+                                       format, type, pixels, unpack,<br>
+                                       true /*for_glTexImage*/);<br>
+   if (ok)<br>
+      return;<br>
+<br>
    /* Attempt to use the blitter for PBO image uploads.<br>
     */<br>
    if (dims <= 2 &&<br>
diff --git a/src/mesa/drivers/dri/intel/intel_tex_subimage.c b/src/mesa/drivers/dri/intel/intel_tex_subimage.c<br>
index ae4b3bc..d932f6e 100644<br>
--- a/src/mesa/drivers/dri/intel/intel_tex_subimage.c<br>
+++ b/src/mesa/drivers/dri/intel/intel_tex_subimage.c<br>
@@ -26,6 +26,7 @@<br>
  *<br>
  **************************************************************************/<br>
<br>
+#include "main/macros.h"<br>
 #include "main/mtypes.h"<br>
 #include "main/pbo.h"<br>
 #include "main/texobj.h"<br>
@@ -148,6 +149,165 @@ intel_blit_texsubimage(struct gl_context * ctx,<br>
    return true;<br>
 }<br>
<br>
+/**<br>
+ * \brief A fast path for glTexImage and glTexSubImage.<br>
+ *<br>
+ * \param for_glTexImage Was this called from glTexImage or glTexSubImage?<br>
+ *<br>
+ * This fast path is taken when the hardware natively supports the texture<br>
+ * format (such as GL_BGRA) and when the texture memory is X-tiled. It uploads<br>
+ * the texture data by mapping the texture memory without a GTT fence, thus<br>
+ * acquiring a tiled view of the memory, and then memcpy'ing sucessive<br>
+ * subspans within each tile.<br>
+ *<br>
+ * This is a performance win over the conventional texture upload path because<br>
+ * it avoids the performance penalty of writing throuh the write-combine<br>
+ * buffer. In the conventional texture upload path,<br>
+ * texstore.c:store_texsubimage(), the texture memory is mapped through a GTT<br>
+ * fence, thus acquiring a linear view of the memory, then each row in the<br>
+ * image is memcpy'd. In this fast path, we replace each row's memcpy with<br>
+ * a sequence of memcpy's over each bit6 swizzle span in the row. A large<br>
+ * memcpy through the write-combine buffer has been observed on Sandybridge to<br>
+ * be approximately 3x slower than a memcpy to normal memory.  So, one would<br>
+ * expect this fast path to be approximately 3x faster than the conventional<br>
+ * texture upload path. This expectation has been confirmed on Sandybridge for<br>
+ * 256x256 GL_BGRA textures.<br>
+ *<br>
+ * This fast path's use case is Google Chrome's paint rectangles.  Chrome (as<br>
+ * of version 21) renders each page as a tiling of 256x256 GL_BGRA textures.<br>
+ * Each page's content is initially uploaded with glTexImage2D and damaged<br>
+ * regions are updated with glTexSubImage2D.<br>
+ */<br>
+bool<br>
+intel_texsubimage_tiled_memcpy(struct gl_context * ctx,<br>
+                               GLuint dims,<br>
+                               struct gl_texture_image *texImage,<br>
+                               GLint xoffset, GLint yoffset, GLint zoffset,<br>
+                               GLsizei width, GLsizei height, GLsizei depth,<br>
+                               GLenum format, GLenum type,<br>
+                               const GLvoid *pixels,<br>
+                               const struct gl_pixelstore_attrib *packing,<br>
+                               bool for_glTexImage)<br>
+{<br>
+   struct intel_context *intel = intel_context(ctx);<br>
+   struct intel_texture_image *image = intel_texture_image(texImage);<br>
+<br>
+   /* Offset into the miptree for the texture image. */<br>
+   uint32_t image_offset_x;<br>
+   uint32_t image_offset_y;<br>
+<br>
+   /* The miptree's buffer. */<br>
+   drm_intel_bo *bo;<br>
+<br>
+   int error = 0;<br>
+<br>
+   /* Restrict to gen >= 6 because the performance impact on earlier chipsets<br>
+    * has not been investigated.<br>
+    *<br>
+    * This function could be expanded to accommodate more textures and<br>
+    * chipets, but more performance inverstigation is needed.<br>
+    */<br>
+   if (intel->gen < 6 ||<br>
+       dims != 2 ||<br>
+       depth != 1 ||<br>
+       format != GL_BGRA ||<br>
+       type != GL_UNSIGNED_BYTE ||<br>
+       pixels == NULL ||<br>
+       packing->Alignment > 4)<br>
+      return false;<br>
+<br>
+   if (for_glTexImage)<br>
+      ctx->Driver.AllocTextureImageBuffer(ctx, texImage);<br>
+<br>
+   if (!image->mt ||<br>
+       image->mt->region->tiling != I915_TILING_X) {<br>
+      /* The algorithm below is written only for X-tiled memory.<br>
+       *<br>
+       * Memcpy'ing to Y-tiled memory is likely faster when done the<br>
+       * conventional way: that is, map the memory through a fence in order to<br>
+       * acquire a linear view of the memory, then memcpy one row at a time.<br>
+       * Due to the arrangement of Y-tiled memory, if we were to upload<br>
+       * a Y-tiled buffer using the algorithm below, then we could only copy<br>
+       * one byte at a time.<br></blockquote><div><br>Nit pick: actually Y tiling only rearranges octwords, so you could copy 16 bytes at a time for y tiling.  Still might not be worth it but one of these days we should probably investigate.<br>
 </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+       */<br>
+      return false;<br>
+   }<br>
+<br>
+   intel_miptree_get_image_offset(image->mt,<br>
+                                  texImage->Level,<br>
+                                  texImage->Face,<br>
+                                  0 /*layer*/,<br>
+                                  &image_offset_x,<br>
+                                  &image_offset_y);<br>
+<br>
+   bo = image->mt->region->bo;<br>
+   error = drm_intel_bo_map(image->mt->region->bo, true /*write_enable*/);<br>
+   if (error || bo->virtual == NULL) {<br>
+      DBG("%s: failed to map bo\n", __FUNCTION__);<br>
+      return false;<br>
+   }<br>
+<br>
+   /* We postponed printing this message until having committed to executing<br>
+    * the function.<br>
+    */<br>
+   DBG("%s: level=%d offset=(%d,%d) (w,h)=(%d,%d)\n",<br>
+       __FUNCTION__, texImage->Level, xoffset, yoffset, width, height);<br>
+<br>
+   /* In the tiling algorithm below, some variables are in units of pixels,<br>
+    * others are in units of bytes, and others (such as height) are unitless.<br>
+    * Each variable name is either prefixed or suffixed with its units.<br>
+    */<br></blockquote><div><br>Thank you for doing this--it makes the code a lot easier to follow.  It looks like everything uses suffixing except pixel_max_x and pixel_max_y.  Could we just change those to max_x_pixels and max_y_pixels?  Also, for consistency it would be nice to rename tile_height to tile_height_pixels.<br>
 </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+   const uint32_t pixel_max_x = xoffset + width;<br>
+   const uint32_t pixel_max_y = yoffset + height;<br>
+<br>
+   const uint32_t tile_size_bytes = 4096;<br>
+<br>
+   const uint32_t tile_width_bytes = 512;<br>
+   const uint32_t tile_width_pixels = 128;<br>
+<br>
+   const uint32_t tile_height = 8;<br>
+<br>
+   const uint32_t cpp = 4; /* chars per pixel of GL_BGRA */<br>
+   const uint32_t swizzle_width_pixels = 16;<br>
+<br>
+   const uint32_t stride_bytes = image->mt->region->pitch * cpp;<br>
+   const uint32_t width_tiles = stride_bytes / tile_width_bytes;<br>
+<br>
+   uint8_t *map = bo->virtual<br>
+               + image_offset_y * stride_bytes<br>
+               + image_offset_x * cpp;<br></blockquote><div><br>I don't think this will work.  The way the miplevel image offsets work is that the parts of the mipmap other than level/layer zero are offset within the existing X tiling pattern.  What you're doing is offsetting the origin of the map assuming no tiling, and then applying the X tiling pattern to the offset image.  In the best case you'll just wind up putting data at the wrong location.  In the worst case there's a danger of going outside the memory allocated for the texture.<br>
 </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+   for (uint32_t pixel_y = yoffset; pixel_y < pixel_max_y; ++pixel_y) {<br>
+      const uint32_t byte_offset_y = (pixel_y / tile_height) * width_tiles * tile_size_bytes<br>
+                                   + (pixel_y % tile_height) * tile_width_bytes;<br>
+<br>
+      for (uint32_t pixel_x = xoffset; pixel_x < pixel_max_x; pixel_x += swizzle_width_pixels) {<br>
+         const uint32_t byte_offset_x = (pixel_x / tile_width_pixels) * tile_size_bytes<br>
+                                      + (pixel_x % tile_width_pixels) * cpp;<br>
+         intptr_t byte_offset = byte_offset_y + byte_offset_x;<br>
+         if (intel->has_swizzling) {<br>
+            bool bit6 = (byte_offset >> 6) & 1;<br>
+            bool bit9 = (byte_offset >> 9) & 1;<br>
+            bool bit10 = (byte_offset >> 10) & 1;<br>
+<br>
+            byte_offset += 64 * (bit9 ^ bit10) * (1 - 2 * bit6);<br></blockquote><div><br>Would this be faster?<br><br>byte_offset ^= ((byte_offset >> 3) ^ (byte_offset >> 4)) & (1 << 6);<br><br></div>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+         }<br>
+<br>
+         const uint32_t swizzle_bound_pixels = ALIGN(pixel_x + 1, swizzle_width_pixels);<br>
+         const uint32_t memcpy_bound_pixels = MIN2(pixel_max_x, swizzle_bound_pixels);<br>
+         const uint32_t copy_size = cpp * (memcpy_bound_pixels - pixel_x);<br>
+<br>
+         memcpy(map + byte_offset, pixels, copy_size);<br>
+         pixels += copy_size;<br>
+         pixel_x -= (pixel_x % swizzle_width_pixels);<br>
+      }<br>
+   }<br>
+<br>
+   drm_intel_bo_unmap(bo);<br>
+   return true;<br>
+}<br>
+<br>
 static void<br>
 intelTexSubImage(struct gl_context * ctx,<br>
                  GLuint dims,<br>
@@ -158,6 +318,16 @@ intelTexSubImage(struct gl_context * ctx,<br>
                  const GLvoid * pixels,<br>
                  const struct gl_pixelstore_attrib *packing)<br>
 {<br>
+   bool ok;<br>
+<br>
+   ok = intel_texsubimage_tiled_memcpy(ctx, dims, texImage,<br>
+                                       xoffset, yoffset, zoffset,<br>
+                                       width, height, depth,<br>
+                                       format, type, pixels, packing,<br>
+                                       false /*for_glTexImage*/);<br>
+   if (ok)<br>
+     return;<br>
+<br>
    /* The intel_blit_texsubimage() function only handles 2D images */<br>
    if (dims != 2 || !intel_blit_texsubimage(ctx, texImage,<br>
                               xoffset, yoffset,<br>
<span class="HOEnZb"><font color="#888888">--<br>
1.7.12<br>
<br>
_______________________________________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/mesa-dev" target="_blank">http://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</font></span></blockquote></div><br>