radeonsi: fix texture format reinterpretation with DCC
authorMarek Olšák <marek.olsak@amd.com>
Mon, 22 Aug 2016 11:45:05 +0000 (13:45 +0200)
committerMarek Olšák <marek.olsak@amd.com>
Mon, 5 Sep 2016 16:01:15 +0000 (18:01 +0200)
DCC is limited in how texture formats can be reinterpreted using texture
views. If we get a view format that is incompatible with the initial
texture format with respect to DCC, disable DCC.

There is a new piglit which tests all format combinations.
What works and what doesn't was deduced by looking at the piglit failures.

Reviewed-by: Bas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
Reviewed-by: Nicolai Hähnle <nicolai.haehnle@amd.com>
src/gallium/drivers/radeon/r600_pipe_common.h
src/gallium/drivers/radeon/r600_texture.c
src/gallium/drivers/radeonsi/si_blit.c
src/gallium/drivers/radeonsi/si_descriptors.c
src/gallium/drivers/radeonsi/si_state.c

index 192453576e55765e05c6231e321b413c19472167..624dea3dbd7a06935ff0aa02a581571260c6d86d 100644 (file)
@@ -757,6 +757,12 @@ bool r600_init_flushed_depth_texture(struct pipe_context *ctx,
 void r600_print_texture_info(struct r600_texture *rtex, FILE *f);
 struct pipe_resource *r600_texture_create(struct pipe_screen *screen,
                                        const struct pipe_resource *templ);
+bool vi_dcc_formats_compatible(enum pipe_format format1,
+                              enum pipe_format format2);
+void vi_dcc_disable_if_incompatible_format(struct r600_common_context *rctx,
+                                          struct pipe_resource *tex,
+                                          unsigned level,
+                                          enum pipe_format view_format);
 struct pipe_surface *r600_create_surface_custom(struct pipe_context *pipe,
                                                struct pipe_resource *texture,
                                                const struct pipe_surface *templ,
index 7bdceb1df9dc0efa541d10bd7926bed6a53ce4f1..2f04019da0235f5df87e62ae1f09dca115c8c9be 100644 (file)
@@ -1666,11 +1666,102 @@ static const struct u_resource_vtbl r600_texture_vtbl =
        r600_texture_transfer_unmap,    /* transfer_unmap */
 };
 
+/* DCC channel type categories within which formats can be reinterpreted
+ * while keeping the same DCC encoding. The swizzle must also match. */
+enum dcc_channel_type {
+       dcc_channel_any32,
+       dcc_channel_int16,
+       dcc_channel_float16,
+       dcc_channel_any_10_10_10_2,
+       dcc_channel_any8,
+       dcc_channel_incompatible,
+};
+
+/* Return the type of DCC encoding. */
+static enum dcc_channel_type
+vi_get_dcc_channel_type(const struct util_format_description *desc)
+{
+       int i;
+
+       /* Find the first non-void channel. */
+       for (i = 0; i < desc->nr_channels; i++)
+               if (desc->channel[i].type != UTIL_FORMAT_TYPE_VOID)
+                       break;
+       if (i == desc->nr_channels)
+               return dcc_channel_incompatible;
+
+       switch (desc->channel[i].size) {
+       case 32:
+               if (desc->nr_channels == 4)
+                       return dcc_channel_incompatible;
+               else
+                       return dcc_channel_any32;
+       case 16:
+               if (desc->channel[i].type == UTIL_FORMAT_TYPE_FLOAT)
+                       return dcc_channel_float16;
+               else
+                       return dcc_channel_int16;
+       case 10:
+               return dcc_channel_any_10_10_10_2;
+       case 8:
+               return dcc_channel_any8;
+       default:
+               return dcc_channel_incompatible;
+       }
+}
+
+/* Return if it's allowed to reinterpret one format as another with DCC enabled. */
+bool vi_dcc_formats_compatible(enum pipe_format format1,
+                              enum pipe_format format2)
+{
+       const struct util_format_description *desc1, *desc2;
+       enum dcc_channel_type type1, type2;
+       int i;
+
+       if (format1 == format2)
+               return true;
+
+       desc1 = util_format_description(format1);
+       desc2 = util_format_description(format2);
+
+       if (desc1->nr_channels != desc2->nr_channels)
+               return false;
+
+       /* Swizzles must be the same. */
+       for (i = 0; i < desc1->nr_channels; i++)
+               if (desc1->swizzle[i] <= PIPE_SWIZZLE_W &&
+                   desc2->swizzle[i] <= PIPE_SWIZZLE_W &&
+                   desc1->swizzle[i] != desc2->swizzle[i])
+                       return false;
+
+       type1 = vi_get_dcc_channel_type(desc1);
+       type2 = vi_get_dcc_channel_type(desc2);
+
+       return type1 != dcc_channel_incompatible &&
+              type2 != dcc_channel_incompatible &&
+              type1 == type2;
+}
+
+void vi_dcc_disable_if_incompatible_format(struct r600_common_context *rctx,
+                                          struct pipe_resource *tex,
+                                          unsigned level,
+                                          enum pipe_format view_format)
+{
+       struct r600_texture *rtex = (struct r600_texture *)tex;
+
+       if (rtex->dcc_offset &&
+           rtex->surface.level[level].dcc_enabled &&
+           !vi_dcc_formats_compatible(tex->format, view_format))
+               if (!r600_texture_disable_dcc(rctx, (struct r600_texture*)tex))
+                       rctx->decompress_dcc(&rctx->b, rtex);
+}
+
 struct pipe_surface *r600_create_surface_custom(struct pipe_context *pipe,
                                                struct pipe_resource *texture,
                                                const struct pipe_surface *templ,
                                                unsigned width, unsigned height)
 {
+       struct r600_common_context *rctx = (struct r600_common_context*)pipe;
        struct r600_texture *rtex = (struct r600_texture*)texture;
        struct r600_surface *surface = CALLOC_STRUCT(r600_surface);
 
@@ -1688,6 +1779,11 @@ struct pipe_surface *r600_create_surface_custom(struct pipe_context *pipe,
        surface->base.height = height;
        surface->base.u = templ->u;
        surface->level_info = &rtex->surface.level[templ->u.tex.level];
+
+       vi_dcc_disable_if_incompatible_format(rctx, texture,
+                                             templ->u.tex.level,
+                                             templ->format);
+
        return &surface->base;
 }
 
index 1147b5b3e22c042e0628fb6b1c76ab6339edb63c..c143601d55c01a7830f061665dde6be270c8564b 100644 (file)
@@ -1124,6 +1124,12 @@ static void si_blit(struct pipe_context *ctx,
 
        /* The driver doesn't decompress resources automatically while
         * u_blitter is rendering. */
+       vi_dcc_disable_if_incompatible_format(&sctx->b, info->src.resource,
+                                             info->src.level,
+                                             info->src.format);
+       vi_dcc_disable_if_incompatible_format(&sctx->b, info->dst.resource,
+                                             info->dst.level,
+                                             info->dst.format);
        si_decompress_subresource(ctx, info->src.resource, info->mask,
                                  info->src.level,
                                  info->src.box.z,
@@ -1153,6 +1159,8 @@ static boolean si_generate_mipmap(struct pipe_context *ctx,
 
        /* The driver doesn't decompress resources automatically while
         * u_blitter is rendering. */
+       vi_dcc_disable_if_incompatible_format(&sctx->b, tex, base_level,
+                                             format);
        si_decompress_subresource(ctx, tex, PIPE_MASK_RGBAZS,
                                  base_level, first_layer, last_layer);
 
index b80f4f25436f20d6e08353f7d05d6f50c1300ba2..b9fae79c57772b9f9a18d3aa175ecaed79e3ef0b 100644 (file)
@@ -653,7 +653,8 @@ static void si_set_shader_image(struct si_context *ctx,
                assert(tex->fmask.size == 0);
 
                if (uses_dcc &&
-                   view->access & PIPE_IMAGE_ACCESS_WRITE) {
+                   (view->access & PIPE_IMAGE_ACCESS_WRITE ||
+                    !vi_dcc_formats_compatible(res->b.b.format, view->format))) {
                        /* If DCC can't be disabled, at least decompress it.
                         * The decompression is relatively cheap if the surface
                         * has been decompressed already.
index 41ff320029418f13a16888e72dbf9ee635b2d30c..f54b0bcc3a87e848ae02934c4aeb6766830fea4f 100644 (file)
@@ -3051,6 +3051,10 @@ si_create_sampler_view_custom(struct pipe_context *ctx,
                }
        }
 
+       vi_dcc_disable_if_incompatible_format(&sctx->b, texture,
+                                             state->u.tex.first_level,
+                                             state->format);
+
        si_make_texture_descriptor(sctx->screen, tmp, true,
                                   state->target, pipe_format, state_swizzle,
                                   first_level, last_level,