[Mesa-dev] [PATCH v5] i965: Fix ETC2/EAC GetCompressed* functions on Gen7 GPUs

Eleni Maria Stea estea at igalia.com
Thu Jun 7 06:34:41 UTC 2018


Gen 7 GPUs store the compressed EAC/ETC2 images in other non-compressed
formats that can render. When GetCompressed* functions are called, the
pixels are returned in the non-compressed format that is used for the
rendering.

With this patch we store both the compressed and non-compressed versions
of the image, so that both rendering commands and GetCompressed*
commands work.

Also, the assertions for GL_MAP_WRITE_BIT and GL_MAP_INVALIDATE_RANGE_BIT
in intel_miptree_map_etc function have been removed because when the
miptree is mapped for reading (for example from a GetCompress*
function) the GL_MAP_WRITE_BIT won't be set (and shouldn't be set).

Fixes: the following test in CTS for gen7:
KHR-GL45.direct_state_access.textures_compressed_subimage test

Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=104272
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=81843

v2: fixes issues:
   a) initialized uninitialized variables (Juan A. Suarez, Andres Gomez)
   b) fixed race condition where mt and cmt were mapped at the same time
   c) fixed indentation issues (Andres Gomez)
v3: adds bugzilla bug with id: 104272
v4: adds bugzilla bug with id: 81843
v5: replaced the flags with a bitfield, refactoring (Kenneth Graunke)
---
 src/mesa/drivers/dri/i965/intel_mipmap_tree.c |  10 +-
 src/mesa/drivers/dri/i965/intel_mipmap_tree.h |  10 ++
 src/mesa/drivers/dri/i965/intel_tex.c         | 106 +++++++++++++++---
 src/mesa/drivers/dri/i965/intel_tex.h         |   1 -
 src/mesa/drivers/dri/i965/intel_tex_image.c   |  46 +++++++-
 src/mesa/drivers/dri/i965/intel_tex_obj.h     |   8 ++
 src/mesa/main/texstore.c                      |  51 ++++++---
 src/mesa/main/texstore.h                      |   8 +-
 8 files changed, 197 insertions(+), 43 deletions(-)

diff --git a/src/mesa/drivers/dri/i965/intel_mipmap_tree.c b/src/mesa/drivers/dri/i965/intel_mipmap_tree.c
index 7d1fa96b91..cc807977de 100644
--- a/src/mesa/drivers/dri/i965/intel_mipmap_tree.c
+++ b/src/mesa/drivers/dri/i965/intel_mipmap_tree.c
@@ -733,9 +733,10 @@ miptree_create(struct brw_context *brw,
    mesa_format etc_format = MESA_FORMAT_NONE;
    uint32_t alloc_flags = 0;
 
-   format = intel_lower_compressed_format(brw, format);
-
-   etc_format = (format != tex_format) ? tex_format : MESA_FORMAT_NONE;
+   if (!(flags & MIPTREE_CREATE_ETC)) {
+      format = intel_lower_compressed_format(brw, format);
+      etc_format = (format != tex_format) ? tex_format : MESA_FORMAT_NONE;
+   }
 
    if (flags & MIPTREE_CREATE_BUSY)
       alloc_flags |= BO_ALLOC_BUSY;
@@ -3372,9 +3373,6 @@ intel_miptree_map_etc(struct brw_context *brw,
       assert(mt->format == MESA_FORMAT_R8G8B8X8_UNORM);
    }
 
-   assert(map->mode & GL_MAP_WRITE_BIT);
-   assert(map->mode & GL_MAP_INVALIDATE_RANGE_BIT);
-
    intel_miptree_access_raw(brw, mt, level, slice, true);
 
    map->stride = _mesa_format_row_stride(mt->etc_format, map->w);
diff --git a/src/mesa/drivers/dri/i965/intel_mipmap_tree.h b/src/mesa/drivers/dri/i965/intel_mipmap_tree.h
index 42f73ba1f9..9e7a401229 100644
--- a/src/mesa/drivers/dri/i965/intel_mipmap_tree.h
+++ b/src/mesa/drivers/dri/i965/intel_mipmap_tree.h
@@ -74,6 +74,7 @@ struct intel_texture_image;
  * without transcoding back.  This flag to intel_miptree_map() gets you that.
  */
 #define BRW_MAP_DIRECT_BIT	0x80000000
+#define BRW_MAP_ETC_BIT	0x40000000
 
 struct intel_miptree_map {
    /** Bitfield of GL_MAP_*_BIT and BRW_MAP_*_BIT. */
@@ -380,6 +381,15 @@ enum intel_miptree_create_flags {
     * that the miptree will be created with mt->aux_usage == NONE.
     */
    MIPTREE_CREATE_NO_AUX   = 1 << 2,
+
+   /** Create a second miptree for the compressed pixels (Gen7 only)
+    *
+    * On Gen7, we need to store 2 miptrees for some compressed
+    * formats so we can handle rendering as well as getting the
+    * compressed image data. This flag indicates that the miptree
+    * is expected to hold compressed data for the latter case.
+    */
+   MIPTREE_CREATE_ETC      = 1 << 3,
 };
 
 struct intel_mipmap_tree *intel_miptree_create(struct brw_context *brw,
diff --git a/src/mesa/drivers/dri/i965/intel_tex.c b/src/mesa/drivers/dri/i965/intel_tex.c
index 0650b6e629..3a94fa7477 100644
--- a/src/mesa/drivers/dri/i965/intel_tex.c
+++ b/src/mesa/drivers/dri/i965/intel_tex.c
@@ -66,6 +66,8 @@ intel_alloc_texture_image_buffer(struct gl_context *ctx,
    struct intel_texture_image *intel_image = intel_texture_image(image);
    struct gl_texture_object *texobj = image->TexObject;
    struct intel_texture_object *intel_texobj = intel_texture_object(texobj);
+   struct gen_device_info *devinfo = &brw->screen->devinfo;
+   mesa_format fmt = image->TexFormat;
 
    assert(image->Border == 0);
 
@@ -110,6 +112,33 @@ intel_alloc_texture_image_buffer(struct gl_context *ctx,
           image->Width, image->Height, image->Depth, intel_image->mt);
    }
 
+   if (devinfo->gen < 8 && _mesa_is_format_etc2(fmt)) {
+      if (intel_texobj->cmt &&
+          intel_miptree_match_image(intel_texobj->cmt, image)) {
+         intel_miptree_reference(&intel_image->cmt, intel_texobj->cmt);
+         DBG("%s: alloc obj %p level %d %dx%dx%d using object's miptree %p\n",
+             __func__, texobj, image->Level,
+             image->Width, image->Height, image->Depth, intel_texobj->cmt);
+      } else {
+         intel_image->cmt = intel_miptree_create_for_teximage(brw,
+                                                              intel_texobj,
+                                                              intel_image,
+                                                              MIPTREE_CREATE_ETC);
+         if (!intel_image->cmt)
+            return false;
+
+         /* Even if the object currently has a mipmap tree associated
+          * with it, this one is a more likely candidate to represent the
+          * whole object since our level didn't fit what was there
+          * before, and any lower levels would fit into our miptree.
+          */
+         intel_miptree_reference(&intel_texobj->cmt, intel_image->cmt);
+
+         DBG("%s: alloc obj %p level %d %dx%dx%d using new miptree %p\n",
+             __func__, texobj, image->Level,
+             image->Width, image->Height, image->Depth, intel_image->cmt);
+      }
+   }
    intel_texobj->needs_validate = true;
 
    return true;
@@ -128,6 +157,7 @@ intel_alloc_texture_storage(struct gl_context *ctx,
                             GLsizei height, GLsizei depth)
 {
    struct brw_context *brw = brw_context(ctx);
+   struct gen_device_info *devinfo = &brw->screen->devinfo;
    struct intel_texture_object *intel_texobj = intel_texture_object(texobj);
    struct gl_texture_image *first_image = texobj->Image[0][0];
    int num_samples = intel_quantize_num_samples(brw->screen,
@@ -136,6 +166,9 @@ intel_alloc_texture_storage(struct gl_context *ctx,
    int face;
    int level;
 
+   mesa_format fmt = first_image->TexFormat;
+   bool is_fake_etc = (devinfo->gen < 8) && _mesa_is_format_etc2(fmt);
+
    /* If the object's current miptree doesn't match what we need, make a new
     * one.
     */
@@ -157,6 +190,22 @@ intel_alloc_texture_storage(struct gl_context *ctx,
       }
    }
 
+   if (is_fake_etc) {
+      if (!intel_texobj->cmt ||
+          !intel_miptree_match_image(intel_texobj->cmt, first_image) ||
+          intel_texobj->cmt->last_level != levels - 1) {
+         intel_miptree_release(&intel_texobj->cmt);
+
+         intel_get_image_dims(first_image, &width, &height, &depth);
+         intel_texobj->cmt = intel_miptree_create(brw, texobj->Target,
+                                                  first_image->TexFormat,
+                                                  0, levels - 1,
+                                                  width, height, depth,
+                                                  MAX2(num_samples, 1),
+                                                  MIPTREE_CREATE_ETC);
+      }
+   }
+
    for (face = 0; face < numFaces; face++) {
       for (level = 0; level < levels; level++) {
          struct gl_texture_image *image = texobj->Image[face][level];
@@ -169,6 +218,8 @@ intel_alloc_texture_storage(struct gl_context *ctx,
             return false;
 
          intel_miptree_reference(&intel_image->mt, intel_texobj->mt);
+         if (is_fake_etc)
+            intel_miptree_reference(&intel_image->cmt, intel_texobj->cmt);
       }
    }
 
@@ -181,7 +232,6 @@ intel_alloc_texture_storage(struct gl_context *ctx,
    return true;
 }
 
-
 static void
 intel_free_texture_image_buffer(struct gl_context * ctx,
 				struct gl_texture_image *texImage)
@@ -191,6 +241,7 @@ intel_free_texture_image_buffer(struct gl_context * ctx,
    DBG("%s\n", __func__);
 
    intel_miptree_release(&intelImage->mt);
+   intel_miptree_release(&intelImage->cmt);
 
    _swrast_free_texture_image_buffer(ctx, texImage);
 }
@@ -204,37 +255,52 @@ intel_free_texture_image_buffer(struct gl_context * ctx,
  */
 static void
 intel_map_texture_image(struct gl_context *ctx,
-			struct gl_texture_image *tex_image,
-			GLuint slice,
-			GLuint x, GLuint y, GLuint w, GLuint h,
-			GLbitfield mode,
-			GLubyte **map,
-			GLint *out_stride)
+                        struct gl_texture_image *tex_image,
+                        GLuint slice,
+                        GLuint x, GLuint y, GLuint w, GLuint h,
+                        GLbitfield mode,
+                        GLubyte **map,
+                        GLint *out_stride)
 {
    struct brw_context *brw = brw_context(ctx);
+   struct gen_device_info *devinfo = &brw->screen->devinfo;
+   mesa_format fmt = tex_image->TexFormat;
    struct intel_texture_image *intel_image = intel_texture_image(tex_image);
    struct intel_mipmap_tree *mt = intel_image->mt;
+   struct intel_mipmap_tree *cmt = intel_image->cmt;
    ptrdiff_t stride;
 
    /* Our texture data is always stored in a miptree. */
    assert(mt);
 
    /* Check that our caller wasn't confused about how to map a 1D texture. */
-   assert(tex_image->TexObject->Target != GL_TEXTURE_1D_ARRAY ||
-	  h == 1);
+   assert(tex_image->TexObject->Target != GL_TEXTURE_1D_ARRAY || h == 1);
 
-   /* intel_miptree_map operates on a unified "slice" number that references the
-    * cube face, since it's all just slices to the miptree code.
+   /* intel_miptree_map operates on a unified "slice" number that references
+    * the cube face, since it's all just slices to the miptree code.
     */
    if (tex_image->TexObject->Target == GL_TEXTURE_CUBE_MAP)
       slice = tex_image->Face;
 
+   if (devinfo->gen < 8) {
+      if ((!(mode & GL_MAP_WRITE_BIT) && _mesa_is_format_etc2(fmt)) ||
+            (mode & BRW_MAP_ETC_BIT)) {
+            assert(cmt);
+            intel_miptree_map(brw, cmt,
+                              tex_image->Level + tex_image->TexObject->MinLevel,
+                              slice + tex_image->TexObject->MinLayer,
+                              x, y, w, h, mode,
+                              (void **)map, &stride);
+            *out_stride = stride;
+            return;
+      }
+   }
+
    intel_miptree_map(brw, mt,
                      tex_image->Level + tex_image->TexObject->MinLevel,
                      slice + tex_image->TexObject->MinLayer,
                      x, y, w, h, mode,
                      (void **)map, &stride);
-
    *out_stride = stride;
 }
 
@@ -243,15 +309,25 @@ intel_unmap_texture_image(struct gl_context *ctx,
 			  struct gl_texture_image *tex_image, GLuint slice)
 {
    struct brw_context *brw = brw_context(ctx);
+
    struct intel_texture_image *intel_image = intel_texture_image(tex_image);
    struct intel_mipmap_tree *mt = intel_image->mt;
+   struct intel_mipmap_tree *cmt = intel_image->cmt;
 
    if (tex_image->TexObject->Target == GL_TEXTURE_CUBE_MAP)
       slice = tex_image->Face;
 
-   intel_miptree_unmap(brw, mt,
-         tex_image->Level + tex_image->TexObject->MinLevel,
-         slice + tex_image->TexObject->MinLayer);
+   if (cmt) {
+      intel_miptree_unmap(brw, cmt,
+                          tex_image->Level + tex_image->TexObject->MinLevel,
+                          slice + tex_image->TexObject->MinLayer);
+   }
+
+   if (mt) {
+      intel_miptree_unmap(brw, mt,
+                          tex_image->Level + tex_image->TexObject->MinLevel,
+                          slice + tex_image->TexObject->MinLayer);
+   }
 }
 
 static GLboolean
diff --git a/src/mesa/drivers/dri/i965/intel_tex.h b/src/mesa/drivers/dri/i965/intel_tex.h
index 4c48875f82..1131ea6009 100644
--- a/src/mesa/drivers/dri/i965/intel_tex.h
+++ b/src/mesa/drivers/dri/i965/intel_tex.h
@@ -54,5 +54,4 @@ intel_miptree_create_for_teximage(struct brw_context *brw,
 
 void intel_finalize_mipmap_tree(struct brw_context *brw,
                                 struct gl_texture_object *tex_obj);
-
 #endif
diff --git a/src/mesa/drivers/dri/i965/intel_tex_image.c b/src/mesa/drivers/dri/i965/intel_tex_image.c
index fae179214d..5cb8adcb7e 100644
--- a/src/mesa/drivers/dri/i965/intel_tex_image.c
+++ b/src/mesa/drivers/dri/i965/intel_tex_image.c
@@ -860,7 +860,7 @@ flush_astc_denorms(struct gl_context *ctx, GLuint dims,
    for (int slice = 0; slice < store.CopySlices; slice++) {
 
       /* Map dest texture buffer */
-      GLubyte *dstMap;
+      GLubyte *dstMap = NULL;
       GLint dstRowStride;
       ctx->Driver.MapTextureImage(ctx, texImage, slice + zoffset,
                                   xoffset, yoffset, width, height,
@@ -904,6 +904,48 @@ flush_astc_denorms(struct gl_context *ctx, GLuint dims,
    }
 }
 
+static void
+intel_store_compressed_texsubimage(struct gl_context *ctx, GLuint dims,
+                                   struct gl_texture_image *intelImage,
+                                   GLint xoffset, GLint yoffset, GLint zoffset,
+                                   GLsizei width, GLsizei height,
+                                   GLsizei depth, GLenum format,
+                                   GLsizei imageSize, const GLvoid *data)
+{
+   struct compressed_pixelstore store;
+   struct brw_context *brw = (struct brw_context*) ctx;
+   const struct gen_device_info *devinfo = &brw->screen->devinfo;
+   GLbitfield mode = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT;
+
+   if (dims == 1) {
+      _mesa_problem(ctx, "Unexpected 1D compressed texsubimage call");
+      return;
+   }
+
+   _mesa_compute_compressed_pixelstore(dims, intelImage->TexFormat,
+                                       width, height, depth,
+                                       &ctx->Unpack, &store);
+
+   /* Get pointer to src pixels (may be in a pbo which we'll map here) */
+   data = _mesa_validate_pbo_compressed_teximage(ctx, dims, imageSize, data,
+                                                 &ctx->Unpack,
+                                                 "glCompressedTexSubImage");
+   if (!data)
+      return;
+
+   _mesa_upload_compressed_texsubimage(ctx, dims, &store, intelImage,
+                                       xoffset, yoffset, zoffset,
+                                       width, height, mode, data);
+
+   if ((devinfo->gen < 8) && _mesa_is_format_etc2(intelImage->TexFormat)) {
+      _mesa_upload_compressed_texsubimage(ctx, dims, &store, intelImage,
+                                          xoffset, yoffset, zoffset,
+                                          width, height,
+                                          mode | BRW_MAP_ETC_BIT, data);
+   }
+
+   _mesa_unmap_teximage_pbo(ctx, &ctx->Unpack);
+}
 
 static void
 intelCompressedTexSubImage(struct gl_context *ctx, GLuint dims,
@@ -914,7 +956,7 @@ intelCompressedTexSubImage(struct gl_context *ctx, GLuint dims,
                         GLsizei imageSize, const GLvoid *data)
 {
    /* Upload the compressed data blocks */
-   _mesa_store_compressed_texsubimage(ctx, dims, texImage,
+   intel_store_compressed_texsubimage(ctx, dims, texImage,
                                       xoffset, yoffset, zoffset,
                                       width, height, depth,
                                       format, imageSize, data);
diff --git a/src/mesa/drivers/dri/i965/intel_tex_obj.h b/src/mesa/drivers/dri/i965/intel_tex_obj.h
index 526f5ceb47..ce4b7a0d36 100644
--- a/src/mesa/drivers/dri/i965/intel_tex_obj.h
+++ b/src/mesa/drivers/dri/i965/intel_tex_obj.h
@@ -50,6 +50,11 @@ struct intel_texture_object
     */
    struct intel_mipmap_tree *mt;
 
+   /* This miptree is used to store the compressed ETC2/EAC pixels
+    * on Gen 7 GPUs for GetCompressed* functions to work.
+    */
+   struct intel_mipmap_tree *cmt;
+
    /**
     * Set when mipmap trees in the texture images of this texture object
     * might not all be the mipmap tree above.
@@ -79,6 +84,9 @@ struct intel_texture_image
     * Else there is no image data.
     */
    struct intel_mipmap_tree *mt;
+
+   /* Stores the ETC2 formatted image on Gen7 GPUs */
+   struct intel_mipmap_tree *cmt;
 };
 
 static inline struct intel_texture_object *
diff --git a/src/mesa/main/texstore.c b/src/mesa/main/texstore.c
index 31163f6771..b441e15852 100644
--- a/src/mesa/main/texstore.c
+++ b/src/mesa/main/texstore.c
@@ -1328,10 +1328,7 @@ _mesa_store_compressed_texsubimage(struct gl_context *ctx, GLuint dims,
                                    GLsizei imageSize, const GLvoid *data)
 {
    struct compressed_pixelstore store;
-   GLint dstRowStride;
-   GLint i, slice;
-   GLubyte *dstMap;
-   const GLubyte *src;
+   GLbitfield mode = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT;
 
    if (dims == 1) {
       _mesa_problem(ctx, "Unexpected 1D compressed texsubimage call");
@@ -1349,41 +1346,59 @@ _mesa_store_compressed_texsubimage(struct gl_context *ctx, GLuint dims,
    if (!data)
       return;
 
-   src = (const GLubyte *) data + store.SkipBytes;
+   _mesa_upload_compressed_texsubimage(ctx, dims, &store, texImage,
+                                       xoffset, yoffset, zoffset,
+                                       width, height, mode, data);
 
-   for (slice = 0; slice < store.CopySlices; slice++) {
+   _mesa_unmap_teximage_pbo(ctx, &ctx->Unpack);
+}
+
+void
+_mesa_upload_compressed_texsubimage(struct gl_context *ctx, GLuint dims,
+                                    struct compressed_pixelstore *store,
+                                    struct gl_texture_image *texImage,
+                                    GLint xoffset, GLint yoffset, GLint zoffset,
+                                    GLsizei width, GLsizei height,
+                                    GLbitfield mode, const GLvoid *data)
+{
+   GLint i, slice, dstRowStride;
+   GLubyte *dstMap = NULL;
+
+   if (!data)
+      return;
+
+   const GLubyte *src = (const GLubyte *) data + store->SkipBytes;
+
+   for (slice = 0; slice < store->CopySlices; slice++) {
       /* Map dest texture buffer */
       ctx->Driver.MapTextureImage(ctx, texImage, slice + zoffset,
                                   xoffset, yoffset, width, height,
-                                  GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT,
+                                  mode,
                                   &dstMap, &dstRowStride);
 
       if (dstMap) {
-
          /* copy rows of blocks */
-         if (dstRowStride == store.TotalBytesPerRow &&
-             dstRowStride == store.CopyBytesPerRow) {
-            memcpy(dstMap, src, store.CopyBytesPerRow * store.CopyRowsPerSlice);
-            src += store.CopyBytesPerRow * store.CopyRowsPerSlice;
+         if (dstRowStride == store->TotalBytesPerRow &&
+             dstRowStride == store->CopyBytesPerRow) {
+            memcpy(dstMap, src, store->CopyBytesPerRow * store->CopyRowsPerSlice);
+            src += store->CopyBytesPerRow * store->CopyRowsPerSlice;
          }
          else {
-            for (i = 0; i < store.CopyRowsPerSlice; i++) {
-               memcpy(dstMap, src, store.CopyBytesPerRow);
+            for (i = 0; i < store->CopyRowsPerSlice; i++) {
+               memcpy(dstMap, src, store->CopyBytesPerRow);
                dstMap += dstRowStride;
-               src += store.TotalBytesPerRow;
+               src += store->TotalBytesPerRow;
             }
          }
 
          ctx->Driver.UnmapTextureImage(ctx, texImage, slice + zoffset);
 
          /* advance to next slice */
-         src += store.TotalBytesPerRow * (store.TotalRowsPerSlice - store.CopyRowsPerSlice);
+         src += store->TotalBytesPerRow * (store->TotalRowsPerSlice - store->CopyRowsPerSlice);
       }
       else {
          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexSubImage%uD",
                      dims);
       }
    }
-
-   _mesa_unmap_teximage_pbo(ctx, &ctx->Unpack);
 }
diff --git a/src/mesa/main/texstore.h b/src/mesa/main/texstore.h
index 2fef7ba7d7..4b197e1641 100644
--- a/src/mesa/main/texstore.h
+++ b/src/mesa/main/texstore.h
@@ -148,7 +148,6 @@ _mesa_store_compressed_texsubimage(struct gl_context *ctx, GLuint dims,
                                    GLenum format,
                                    GLsizei imageSize, const GLvoid *data);
 
-
 struct compressed_pixelstore {
    int SkipBytes;
    int CopyBytesPerRow;
@@ -158,6 +157,13 @@ struct compressed_pixelstore {
    int CopySlices;
 };
 
+extern void
+_mesa_upload_compressed_texsubimage(struct gl_context *ctx, GLuint dims,
+                                    struct compressed_pixelstore *store,
+                                    struct gl_texture_image *texImage,
+                                    GLint xoffset, GLint yoffset, GLint zoffset,
+                                    GLsizei width, GLsizei height,
+                                    GLbitfield mode, const GLvoid *data);
 
 extern void
 _mesa_compute_compressed_pixelstore(GLuint dims, mesa_format texFormat,
-- 
2.17.0



More information about the mesa-dev mailing list