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

Eleni Maria Stea estea at igalia.com
Wed Jul 18 12:04:00 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)
v6: renamed the r8stencil_mt secondary miptree that is now part of the
    intel_miptree_struct to shadow_mt and used it to store the compressed
    miptree (Nanley Chery)
---
 .../drivers/dri/i965/brw_wm_surface_state.c   |  8 +-
 src/mesa/drivers/dri/i965/intel_mipmap_tree.c | 27 +++---
 src/mesa/drivers/dri/i965/intel_mipmap_tree.h | 14 ++-
 src/mesa/drivers/dri/i965/intel_tex.c         | 90 ++++++++++++++++++-
 src/mesa/drivers/dri/i965/intel_tex_image.c   | 46 +++++++++-
 src/mesa/main/texstore.c                      | 62 ++++++++-----
 src/mesa/main/texstore.h                      |  8 ++
 7 files changed, 209 insertions(+), 46 deletions(-)

diff --git a/src/mesa/drivers/dri/i965/brw_wm_surface_state.c b/src/mesa/drivers/dri/i965/brw_wm_surface_state.c
index 9397b637c7..2097fabaeb 100644
--- a/src/mesa/drivers/dri/i965/brw_wm_surface_state.c
+++ b/src/mesa/drivers/dri/i965/brw_wm_surface_state.c
@@ -563,15 +563,15 @@ static void brw_update_texture_surface(struct gl_context *ctx,
 
       if (obj->StencilSampling && firstImage->_BaseFormat == GL_DEPTH_STENCIL) {
          if (devinfo->gen <= 7) {
-            assert(mt->r8stencil_mt && !mt->stencil_mt->r8stencil_needs_update);
-            mt = mt->r8stencil_mt;
+            assert(mt->shadow_mt && !mt->stencil_mt->shadow_needs_update);
+            mt = mt->shadow_mt;
          } else {
             mt = mt->stencil_mt;
          }
          format = ISL_FORMAT_R8_UINT;
       } else if (devinfo->gen <= 7 && mt->format == MESA_FORMAT_S_UINT8) {
-         assert(mt->r8stencil_mt && !mt->r8stencil_needs_update);
-         mt = mt->r8stencil_mt;
+         assert(mt->shadow_mt && !mt->shadow_needs_update);
+         mt = mt->shadow_mt;
          format = ISL_FORMAT_R8_UINT;
       }
 
diff --git a/src/mesa/drivers/dri/i965/intel_mipmap_tree.c b/src/mesa/drivers/dri/i965/intel_mipmap_tree.c
index 7b1f0896ae..6d07fede52 100644
--- a/src/mesa/drivers/dri/i965/intel_mipmap_tree.c
+++ b/src/mesa/drivers/dri/i965/intel_mipmap_tree.c
@@ -719,8 +719,12 @@ miptree_create(struct brw_context *brw,
       }
    }
 
-   mt->etc_format = (_mesa_is_format_color_format(format) && mt_fmt != format) ?
-                    format : MESA_FORMAT_NONE;
+   if (!(flags & MIPTREE_CREATE_ETC)) {
+      mt->etc_format = (_mesa_is_format_color_format(format) &&
+                        mt_fmt != format) ? format : MESA_FORMAT_NONE;
+   } else {
+      mt->etc_format = MESA_FORMAT_NONE;
+   }
 
    if (!(flags & MIPTREE_CREATE_NO_AUX))
       intel_miptree_choose_aux_usage(brw, mt);
@@ -1214,7 +1218,7 @@ intel_miptree_release(struct intel_mipmap_tree **mt)
 
       brw_bo_unreference((*mt)->bo);
       intel_miptree_release(&(*mt)->stencil_mt);
-      intel_miptree_release(&(*mt)->r8stencil_mt);
+      intel_miptree_release(&(*mt)->shadow_mt);
       intel_miptree_aux_buffer_free((*mt)->aux_buf);
       free_aux_state_map((*mt)->aux_state);
 
@@ -2426,7 +2430,7 @@ intel_miptree_finish_write(struct brw_context *brw,
    switch (mt->aux_usage) {
    case ISL_AUX_USAGE_NONE:
       if (mt->format == MESA_FORMAT_S_UINT8 && devinfo->gen <= 7)
-         mt->r8stencil_needs_update = true;
+         mt->shadow_needs_update = true;
       break;
 
    case ISL_AUX_USAGE_MCS:
@@ -2919,9 +2923,9 @@ intel_update_r8stencil(struct brw_context *brw,
 
    assert(src->surf.size > 0);
 
-   if (!mt->r8stencil_mt) {
+   if (!mt->shadow_mt) {
       assert(devinfo->gen > 6); /* Handle MIPTREE_LAYOUT_GEN6_HIZ_STENCIL */
-      mt->r8stencil_mt = make_surface(
+      mt->shadow_mt = make_surface(
                             brw,
                             src->target,
                             MESA_FORMAT_R_UINT8,
@@ -2935,13 +2939,13 @@ intel_update_r8stencil(struct brw_context *brw,
                             ISL_TILING_Y0_BIT,
                             ISL_SURF_USAGE_TEXTURE_BIT,
                             BO_ALLOC_BUSY, 0, NULL);
-      assert(mt->r8stencil_mt);
+      assert(mt->shadow_mt);
    }
 
-   if (src->r8stencil_needs_update == false)
+   if (src->shadow_needs_update == false)
       return;
 
-   struct intel_mipmap_tree *dst = mt->r8stencil_mt;
+   struct intel_mipmap_tree *dst = mt->shadow_mt;
 
    for (int level = src->first_level; level <= src->last_level; level++) {
       const unsigned depth = src->surf.dim == ISL_SURF_DIM_3D ?
@@ -2961,7 +2965,7 @@ intel_update_r8stencil(struct brw_context *brw,
    }
 
    brw_cache_flush_for_read(brw, dst->bo);
-   src->r8stencil_needs_update = false;
+   src->shadow_needs_update = false;
 }
 
 static void *
@@ -3333,9 +3337,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 bb7df7ad23..5f4cdd28d8 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. */
@@ -302,8 +303,8 @@ struct intel_mipmap_tree
     *
     * \see intel_update_r8stencil()
     */
-   struct intel_mipmap_tree *r8stencil_mt;
-   bool r8stencil_needs_update;
+   struct intel_mipmap_tree *shadow_mt;
+   bool shadow_needs_update;
 
    /**
     * \brief CCS, MCS, or HiZ auxiliary buffer.
@@ -377,6 +378,15 @@ enum intel_miptree_create_flags {
     * that the miptree will be created with mt->aux_usage == NONE.
     */
    MIPTREE_CREATE_NO_AUX   = 1 << 1,
+
+   /** 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 << 2,
 };
 
 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..922c6aa4bc 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,38 @@ 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->mt->shadow_mt &&
+          intel_miptree_match_image(intel_texobj->mt->shadow_mt,
+                                    image)) {
+         intel_miptree_reference(&intel_texobj->mt->shadow_mt,
+                                 intel_image->mt->shadow_mt);
+         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->mt->shadow_mt);
+      } else {
+         intel_image->mt->shadow_mt = intel_miptree_create_for_teximage(brw,
+                                                                        intel_texobj,
+                                                                        intel_image,
+                                                                        MIPTREE_CREATE_ETC);
+         if (!intel_image->mt->shadow_mt)
+            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->mt->shadow_mt,
+                                 intel_image->mt->shadow_mt);
+
+         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->mt->shadow_mt);
+      }
+   }
+
    intel_texobj->needs_validate = true;
 
    return true;
@@ -128,6 +162,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 +171,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 +195,24 @@ intel_alloc_texture_storage(struct gl_context *ctx,
       }
    }
 
+   if (is_fake_etc) {
+      if (!intel_texobj->mt->shadow_mt ||
+          !intel_miptree_match_image(intel_texobj->mt->shadow_mt,
+                                     first_image) ||
+          intel_texobj->mt->shadow_mt->last_level != levels -1) {
+         intel_miptree_release(&intel_texobj->mt->shadow_mt);
+         intel_get_image_dims(first_image, &width, &height, &depth);
+         intel_texobj->mt->shadow_mt = 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 +225,9 @@ 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->mt->shadow_mt,
+                                    intel_texobj->mt->shadow_mt);
       }
    }
 
@@ -212,8 +271,11 @@ intel_map_texture_image(struct gl_context *ctx,
 			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->mt->shadow_mt;
    ptrdiff_t stride;
 
    /* Our texture data is always stored in a miptree. */
@@ -229,6 +291,20 @@ intel_map_texture_image(struct gl_context *ctx,
    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,
@@ -245,13 +321,21 @@ intel_unmap_texture_image(struct gl_context *ctx,
    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->mt->shadow_mt;
 
    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_image.c b/src/mesa/drivers/dri/i965/intel_tex_image.c
index 3d948381f4..710d93cd06 100644
--- a/src/mesa/drivers/dri/i965/intel_tex_image.c
+++ b/src/mesa/drivers/dri/i965/intel_tex_image.c
@@ -857,7 +857,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,
@@ -901,6 +901,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,
@@ -911,7 +953,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/main/texstore.c b/src/mesa/main/texstore.c
index 31163f6771..7ceb590dce 100644
--- a/src/mesa/main/texstore.c
+++ b/src/mesa/main/texstore.c
@@ -1322,16 +1322,15 @@ _mesa_compute_compressed_pixelstore(GLuint dims, mesa_format texFormat,
 void
 _mesa_store_compressed_texsubimage(struct gl_context *ctx, GLuint dims,
                                    struct gl_texture_image *texImage,
-                                   GLint xoffset, GLint yoffset, GLint zoffset,
-                                   GLsizei width, GLsizei height, GLsizei depth,
+                                   GLint xoffset, GLint yoffset,
+                                   GLint zoffset,
+                                   GLsizei width, GLsizei height,
+                                   GLsizei depth,
                                    GLenum format,
                                    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 +1348,60 @@ _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);
+
+   _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;
 
-   for (slice = 0; slice < store.CopySlices; slice++) {
+   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,
+                                  xoffset, yoffset, width, height, 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..d40230e885 100644
--- a/src/mesa/main/texstore.h
+++ b/src/mesa/main/texstore.h
@@ -158,6 +158,14 @@ 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.18.0



More information about the mesa-dev mailing list