[Mesa-dev] [PATCH v2 3/5] i965: Faking the ETC2 compression on Gen < 8 GPUs using two miptrees.

Eleni Maria Stea estea at igalia.com
Sun Feb 3 13:07:34 UTC 2019


GPUs Gen < 8 cannot sample ETC2 formats. So far, they converted the
compressed EAC/ETC2 images to non-compressed RGBA images. When
GetCompressed* functions were called, the pixels were returned in this
RGBA format and not the compressed format that was expected.

Trying to fix this problem, we use a secondary shadow miptree to store the
decompressed data for the rendering and the main miptree to store the
compressed for the Get functions to work. Each time that the main miptree
is written with compressed data, we decompress them to RGB and update the
shadow. Then we use the shadow for rendering.

v2:
   - Fixes in the commit message (Nanley Chery)
   - Reversed the changes in brw_get_texture_swizzle and swapped the b, g
   values at the time that we decompress the data in the function:
   intel_miptree_update_etc_shadow of intel_mipmap_tree.c (Nanley Chery)
   - Simplified the format checks in the miptree_create function of the
   intel_mipmap_tree.c and reserved the call of the
   intel_lower_compressed_format for the case that we are faking the ETC
   support (Nanley Chery)
   - Removed the check for the auxiliary usage for the shadow miptree at
   creation (miptree_create of intel_mipmap_tree.c) as we won't use
   auxiliary buffers with these types of trees (Nanley Chery)
   - Set the etc_format of the non-ETC miptrees to MESA_FORMAT_NONE and
   removed the unecessary checks (Nanley Chery)
   - Fixed an unrelated indentation change (Nanley Chery)
   - Modified the function intel_miptree_finish_write to set the
   mt->shadow_needs_update to true to catch all the cases when we need to
   update the miptree (Nanley Chery)
   - In order to update the shadow miptree during the unmap of the
   main and always map the main (Nanley Chery) the following change was
   necessary: Splitted the previous update function that was updating all
   the mipmap levels and use two functions instead: one that updates one
   level and one that updates all of them. Used the first during unmap
   and the second before the rendering.
   - Removed the BRW_MAP_ETC_BIT flag and the mechanism to decide which
   miptree should be mapped each time and reversed all the changes in the
   higher level texture functions that upload data to textures as they
   aren't needed anymore.
   - Replaced the boolean needs_fake_etc with an inline function that
   checks when we need to fake the ETC compression (Nanley Chery)
   - Removed the initialization of the strides in the update function as
   the values will be overwritten by the intel_miptree_map call (Nanley
   Chery)
   - Used minify instead of division in the new update function
   intel_miptree_update_etc_shadow_levels in intel_mipmap_tree.c (Nanley
   Chery)
   - Removed the depth from the calculation of the number of slices in
   the new update function (intel_miptree_update_etc_shadow_levels of
   intel_mipmap_tree.c) as we don't need to support 3D ETC images.
   (Nanley Chery)
---
 .../drivers/dri/i965/brw_wm_surface_state.c   |   5 +-
 src/mesa/drivers/dri/i965/intel_mipmap_tree.c | 133 ++++++++++++++++--
 src/mesa/drivers/dri/i965/intel_mipmap_tree.h |  22 +++
 3 files changed, 150 insertions(+), 10 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 618e2ab35bc..c2cf34aee71 100644
--- a/src/mesa/drivers/dri/i965/brw_wm_surface_state.c
+++ b/src/mesa/drivers/dri/i965/brw_wm_surface_state.c
@@ -521,7 +521,7 @@ static void brw_update_texture_surface(struct gl_context *ctx,
           */
          mesa_fmt = mt->format;
       } else if (mt->etc_format != MESA_FORMAT_NONE) {
-         mesa_fmt = mt->format;
+         mesa_fmt = mt->shadow_mt->format;
       } else if (plane > 0) {
          mesa_fmt = mt->format;
       } else {
@@ -581,6 +581,9 @@ static void brw_update_texture_surface(struct gl_context *ctx,
          assert(mt->shadow_mt && !mt->shadow_needs_update);
          mt = mt->shadow_mt;
          format = ISL_FORMAT_R8_UINT;
+      } else if (intel_miptree_needs_fake_etc(brw, mt)) {
+         assert(mt->shadow_mt);
+         mt = mt->shadow_mt;
       }
 
       const int surf_index = surf_offset - &brw->wm.base.surf_offset[0];
diff --git a/src/mesa/drivers/dri/i965/intel_mipmap_tree.c b/src/mesa/drivers/dri/i965/intel_mipmap_tree.c
index 0a25dfd0161..3ff36b84a5a 100644
--- a/src/mesa/drivers/dri/i965/intel_mipmap_tree.c
+++ b/src/mesa/drivers/dri/i965/intel_mipmap_tree.c
@@ -57,6 +57,11 @@ static void *intel_miptree_map_raw(struct brw_context *brw,
                                    GLbitfield mode);
 
 static void intel_miptree_unmap_raw(struct intel_mipmap_tree *mt);
+static void intel_miptree_update_etc_shadow(struct brw_context *brw,
+                                            struct intel_mipmap_tree *mt,
+                                            unsigned int level,
+                                            unsigned int slice,
+                                            int level_w, int level_h);
 
 static bool
 intel_miptree_supports_mcs(struct brw_context *brw,
@@ -687,10 +692,8 @@ miptree_create(struct brw_context *brw,
    if (devinfo->gen < 6 && _mesa_is_format_color_format(format))
       tiling_flags &= ~ISL_TILING_Y0_BIT;
 
-   mesa_format mt_fmt;
-   if (_mesa_is_format_color_format(format)) {
-      mt_fmt = intel_lower_compressed_format(brw, format);
-   } else {
+   mesa_format mt_fmt = format;
+   if (!_mesa_is_format_color_format(format)) {
       /* Fix up the Z miptree format for how we're splitting out separate
        * stencil. Gen7 expects there to be no stencil bits in its depth buffer.
        */
@@ -707,6 +710,25 @@ miptree_create(struct brw_context *brw,
    if (mt == NULL)
       return NULL;
 
+   if (intel_miptree_needs_fake_etc(brw, mt)) {
+      mesa_format rgba_fmt = intel_lower_compressed_format(brw, format);
+      mt->etc_format = format;
+      mt->shadow_mt = make_surface(brw, target, rgba_fmt, first_level,
+                                   last_level, width0, height0, depth0,
+                                   num_samples, tiling_flags,
+                                   mt_surf_usage(mt_fmt),
+                                   alloc_flags, 0, NULL);
+
+      if (mt->shadow_mt == NULL) {
+         intel_miptree_release(&mt);
+         return NULL;
+      }
+
+      mt->shadow_mt->etc_format = MESA_FORMAT_NONE;
+   } else {
+      mt->etc_format = MESA_FORMAT_NONE;
+   }
+
    if (needs_separate_stencil(brw, mt, format)) {
       mt->stencil_mt =
          make_surface(brw, target, MESA_FORMAT_S_UINT8, first_level, last_level,
@@ -719,9 +741,6 @@ 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_NO_AUX))
       intel_miptree_choose_aux_usage(brw, mt);
 
@@ -2426,8 +2445,11 @@ 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)
+      if (mt->format == MESA_FORMAT_S_UINT8 && devinfo->gen <= 7) {
+         mt->shadow_needs_update = true;
+      } else if (intel_miptree_has_etc_shadow(brw, mt)) {
          mt->shadow_needs_update = true;
+      }
       break;
 
    case ISL_AUX_USAGE_MCS:
@@ -3779,7 +3801,8 @@ intel_miptree_map(struct brw_context *brw,
    if (mt->format == MESA_FORMAT_S_UINT8) {
       intel_miptree_map_s8(brw, mt, map, level, slice);
    } else if (mt->etc_format != MESA_FORMAT_NONE &&
-              !(mode & BRW_MAP_DIRECT_BIT)) {
+              !(mode & BRW_MAP_DIRECT_BIT) &&
+              !(intel_miptree_needs_fake_etc(brw, mt))) {
       intel_miptree_map_etc(brw, mt, map, level, slice);
    } else if (mt->stencil_mt && !(mode & BRW_MAP_DIRECT_BIT)) {
       intel_miptree_map_depthstencil(brw, mt, map, level, slice);
@@ -3813,6 +3836,7 @@ intel_miptree_unmap(struct brw_context *brw,
                     unsigned int slice)
 {
    struct intel_miptree_map *map = mt->level[level].slice[slice].map;
+   int level_w, level_h;
 
    assert(mt->surf.samples == 1);
 
@@ -3822,10 +3846,20 @@ intel_miptree_unmap(struct brw_context *brw,
    DBG("%s: mt %p (%s) level %d slice %d\n", __func__,
        mt, _mesa_get_format_name(mt->format), level, slice);
 
+   level_w = minify(mt->surf.phys_level0_sa.width,
+                    level - mt->first_level);
+   level_h = minify(mt->surf.phys_level0_sa.height,
+                    level - mt->first_level);
+
    if (map->unmap)
 	   map->unmap(brw, mt, map, level, slice);
 
    intel_miptree_release_map(mt, level, slice);
+
+   if (intel_miptree_has_etc_shadow(brw, mt) && mt->shadow_needs_update) {
+      intel_miptree_update_etc_shadow(brw, mt, level, slice, level_w,
+                                      level_h);
+   }
 }
 
 enum isl_surf_dim
@@ -3940,3 +3974,84 @@ intel_miptree_get_clear_color(const struct gen_device_info *devinfo,
       return mt->fast_clear_color;
    }
 }
+
+static void
+intel_miptree_update_etc_shadow(struct brw_context *brw,
+                                struct intel_mipmap_tree *mt,
+                                unsigned int level,
+                                unsigned int slice,
+                                int level_w,
+                                int level_h)
+{
+   struct intel_mipmap_tree *smt;
+   ptrdiff_t etc_stride, shadow_stride;
+   GLbitfield etc_mode, shadow_mode;
+   void *mptr, *sptr;
+
+   assert(intel_miptree_has_etc_shadow(brw, mt));
+   if (!mt->shadow_needs_update)
+      return;
+
+   mt->shadow_needs_update = false;
+   smt = mt->shadow_mt;
+
+   etc_mode = GL_MAP_READ_BIT | BRW_MAP_DIRECT_BIT;
+   shadow_mode = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT |
+                 GL_MAP_INVALIDATE_RANGE_BIT;
+
+   intel_miptree_map(brw, mt, level, slice, 0, 0, level_w, level_h,
+                     etc_mode, &mptr, &etc_stride);
+   intel_miptree_map(brw, smt, level, slice, 0, 0, level_w, level_h,
+                     shadow_mode, &sptr, &shadow_stride);
+
+   if (mt->format == MESA_FORMAT_ETC1_RGB8) {
+      _mesa_etc1_unpack_rgba8888(sptr, shadow_stride, mptr, etc_stride,
+                                 level_w, level_h);
+   } else {
+      /* set bgra to false to match the rgba swizzle */
+      bool bgra = false;
+      if (smt->format == MESA_FORMAT_B8G8R8A8_SRGB)
+         bgra = true;
+      _mesa_unpack_etc2_format(sptr, shadow_stride, mptr, etc_stride,
+                               level_w, level_h, mt->format, bgra);
+   }
+
+   intel_miptree_unmap(brw, mt, level, slice);
+   intel_miptree_unmap(brw, smt, level, slice);
+}
+
+void
+intel_miptree_update_etc_shadow_levels(struct brw_context *brw,
+                                       struct intel_mipmap_tree *mt)
+{
+   struct intel_mipmap_tree *smt;
+   int num_slices;
+   int level_w, level_h;
+
+   assert(mt);
+   assert(mt->surf.size_B > 0);
+
+   assert(intel_miptree_has_etc_shadow(brw, mt));
+
+   smt = mt->shadow_mt;
+
+   level_w = smt->surf.logical_level0_px.width;
+   level_h = smt->surf.logical_level0_px.height;
+
+   num_slices = smt->surf.logical_level0_px.array_len;
+
+   for (int level = smt->first_level; level <= smt->last_level; level++)
+   {
+      for (unsigned int slice = 0; slice < num_slices; slice++) {
+         intel_miptree_update_etc_shadow(brw, mt, level, slice, level_w,
+                                         level_h);
+      }
+
+      level_w = minify(mt->surf.phys_level0_sa.width,
+                       level - mt->first_level);
+      level_h = minify(mt->surf.phys_level0_sa.height,
+                       level - mt->first_level);
+   }
+
+   mt->shadow_needs_update = false;
+}
diff --git a/src/mesa/drivers/dri/i965/intel_mipmap_tree.h b/src/mesa/drivers/dri/i965/intel_mipmap_tree.h
index 1a7507023a1..30bc279ee42 100644
--- a/src/mesa/drivers/dri/i965/intel_mipmap_tree.h
+++ b/src/mesa/drivers/dri/i965/intel_mipmap_tree.h
@@ -730,6 +730,28 @@ isl_memcpy_type
 intel_miptree_get_memcpy_type(mesa_format tiledFormat, GLenum format, GLenum type,
                               uint32_t *cpp);
 
+static inline bool
+intel_miptree_needs_fake_etc(struct brw_context *brw,
+                             struct intel_mipmap_tree *mt)
+{
+   const struct gen_device_info *devinfo = &brw->screen->devinfo;
+   bool is_etc = _mesa_is_format_etc2(mt->format) ||
+                 (mt->format == MESA_FORMAT_ETC1_RGB8);
+
+   return devinfo->gen < 8 && is_etc;
+}
+
+static inline bool
+intel_miptree_has_etc_shadow(struct brw_context *brw,
+                             struct intel_mipmap_tree *mt)
+{
+   return intel_miptree_needs_fake_etc(brw, mt) && mt->shadow_mt;
+}
+
+void
+intel_miptree_update_etc_shadow_levels(struct brw_context *brw,
+                                       struct intel_mipmap_tree *mt);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.20.1



More information about the mesa-dev mailing list