From 21de3be8e62b2b093569a99550e6356ed2f106b4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marek=20Ol=C5=A1=C3=A1k?= Date: Mon, 22 Aug 2016 13:45:05 +0200 Subject: [PATCH] radeonsi: fix texture format reinterpretation with DCC MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Nicolai Hähnle --- src/gallium/drivers/radeon/r600_pipe_common.h | 6 ++ src/gallium/drivers/radeon/r600_texture.c | 96 +++++++++++++++++++ src/gallium/drivers/radeonsi/si_blit.c | 8 ++ src/gallium/drivers/radeonsi/si_descriptors.c | 3 +- src/gallium/drivers/radeonsi/si_state.c | 4 + 5 files changed, 116 insertions(+), 1 deletion(-) diff --git a/src/gallium/drivers/radeon/r600_pipe_common.h b/src/gallium/drivers/radeon/r600_pipe_common.h index 192453576e5..624dea3dbd7 100644 --- a/src/gallium/drivers/radeon/r600_pipe_common.h +++ b/src/gallium/drivers/radeon/r600_pipe_common.h @@ -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, diff --git a/src/gallium/drivers/radeon/r600_texture.c b/src/gallium/drivers/radeon/r600_texture.c index 7bdceb1df9d..2f04019da02 100644 --- a/src/gallium/drivers/radeon/r600_texture.c +++ b/src/gallium/drivers/radeon/r600_texture.c @@ -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; } diff --git a/src/gallium/drivers/radeonsi/si_blit.c b/src/gallium/drivers/radeonsi/si_blit.c index 1147b5b3e22..c143601d55c 100644 --- a/src/gallium/drivers/radeonsi/si_blit.c +++ b/src/gallium/drivers/radeonsi/si_blit.c @@ -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); diff --git a/src/gallium/drivers/radeonsi/si_descriptors.c b/src/gallium/drivers/radeonsi/si_descriptors.c index b80f4f25436..b9fae79c577 100644 --- a/src/gallium/drivers/radeonsi/si_descriptors.c +++ b/src/gallium/drivers/radeonsi/si_descriptors.c @@ -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. diff --git a/src/gallium/drivers/radeonsi/si_state.c b/src/gallium/drivers/radeonsi/si_state.c index 41ff3200294..f54b0bcc3a8 100644 --- a/src/gallium/drivers/radeonsi/si_state.c +++ b/src/gallium/drivers/radeonsi/si_state.c @@ -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, -- 2.30.2