[Mesa-dev] [PATCH 11/20] radeonsi: fix texture format reinterpretation with DCC

Marek Olšák maraeo at gmail.com
Mon Aug 29 15:28:26 UTC 2016


From: Marek Olšák <marek.olsak at amd.com>

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.
---
 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 1924535..624dea3 100644
--- a/src/gallium/drivers/radeon/r600_pipe_common.h
+++ b/src/gallium/drivers/radeon/r600_pipe_common.h
@@ -750,20 +750,26 @@ void r600_texture_get_fmask_info(struct r600_common_screen *rscreen,
 				 struct r600_fmask_info *out);
 void r600_texture_get_cmask_info(struct r600_common_screen *rscreen,
 				 struct r600_texture *rtex,
 				 struct r600_cmask_info *out);
 bool r600_init_flushed_depth_texture(struct pipe_context *ctx,
 				     struct pipe_resource *texture,
 				     struct r600_texture **staging);
 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,
 						unsigned width, unsigned height);
 unsigned r600_translate_colorswap(enum pipe_format format, bool do_endian_swap);
 void vi_separate_dcc_start_query(struct pipe_context *ctx,
 				 struct r600_texture *tex);
 void vi_separate_dcc_stop_query(struct pipe_context *ctx,
 				struct r600_texture *tex);
 void vi_separate_dcc_process_and_reset_stats(struct pipe_context *ctx,
diff --git a/src/gallium/drivers/radeon/r600_texture.c b/src/gallium/drivers/radeon/r600_texture.c
index 7bdceb1..2f04019 100644
--- a/src/gallium/drivers/radeon/r600_texture.c
+++ b/src/gallium/drivers/radeon/r600_texture.c
@@ -1659,42 +1659,138 @@ static void r600_texture_transfer_unmap(struct pipe_context *ctx,
 
 static const struct u_resource_vtbl r600_texture_vtbl =
 {
 	NULL,				/* get_handle */
 	r600_texture_destroy,		/* resource_destroy */
 	r600_texture_transfer_map,	/* transfer_map */
 	u_default_transfer_flush_region, /* transfer_flush_region */
 	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);
 
 	if (!surface)
 		return NULL;
 
 	assert(templ->u.tex.first_layer <= util_max_layer(texture, templ->u.tex.level));
 	assert(templ->u.tex.last_layer <= util_max_layer(texture, templ->u.tex.level));
 
 	pipe_reference_init(&surface->base.reference, 1);
 	pipe_resource_reference(&surface->base.texture, texture);
 	surface->base.context = pipe;
 	surface->base.format = templ->format;
 	surface->base.width = width;
 	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;
 }
 
 static struct pipe_surface *r600_create_surface(struct pipe_context *pipe,
 						struct pipe_resource *tex,
 						const struct pipe_surface *templ)
 {
 	unsigned level = templ->u.tex.level;
 	unsigned width = u_minify(tex->width0, level);
 	unsigned height = u_minify(tex->height0, level);
diff --git a/src/gallium/drivers/radeonsi/si_blit.c b/src/gallium/drivers/radeonsi/si_blit.c
index 1147b5b..c143601 100644
--- a/src/gallium/drivers/radeonsi/si_blit.c
+++ b/src/gallium/drivers/radeonsi/si_blit.c
@@ -1117,20 +1117,26 @@ static void si_blit(struct pipe_context *ctx,
 				 info->dst.box.z,
 				 info->src.resource, info->src.level,
 				 &info->src.box);
 		return;
 	}
 
 	assert(util_blitter_is_blit_supported(sctx->blitter, info));
 
 	/* 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,
 				  info->src.box.z + info->src.box.depth - 1);
 
 	if (sctx->screen->b.debug_flags & DBG_FORCE_DMA &&
 	    util_try_blit_via_copy_region(ctx, info))
 		return;
 
 	si_blitter_begin(ctx, SI_BLIT |
@@ -1146,20 +1152,22 @@ static boolean si_generate_mipmap(struct pipe_context *ctx,
 				  unsigned first_layer, unsigned last_layer)
 {
 	struct si_context *sctx = (struct si_context*)ctx;
 	struct r600_texture *rtex = (struct r600_texture *)tex;
 
 	if (!util_blitter_is_copy_supported(sctx->blitter, tex, tex))
 		return false;
 
 	/* 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);
 
 	/* Clear dirty_level_mask for the levels that will be overwritten. */
 	assert(base_level < last_level);
 	rtex->dirty_level_mask &= ~u_bit_consecutive(base_level + 1,
 						     last_level - base_level);
 
 	si_blitter_begin(ctx, SI_BLIT | SI_DISABLE_RENDER_COND);
 	util_blitter_generate_mipmap(sctx->blitter, tex, format,
diff --git a/src/gallium/drivers/radeonsi/si_descriptors.c b/src/gallium/drivers/radeonsi/si_descriptors.c
index b3174c6..c150175 100644
--- a/src/gallium/drivers/radeonsi/si_descriptors.c
+++ b/src/gallium/drivers/radeonsi/si_descriptors.c
@@ -646,21 +646,22 @@ static void si_set_shader_image(struct si_context *ctx,
 		unsigned level = view->u.tex.level;
 		unsigned width, height, depth;
 		uint32_t *desc = descs->list + slot * 8;
 		bool uses_dcc = tex->dcc_offset &&
 				tex->surface.level[level].dcc_enabled;
 
 		assert(!tex->is_depth);
 		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.
 			 */
 			if (r600_texture_disable_dcc(&ctx->b, tex))
 				uses_dcc = false;
 			else
 				ctx->b.decompress_dcc(&ctx->b.b, tex);
 		}
 
diff --git a/src/gallium/drivers/radeonsi/si_state.c b/src/gallium/drivers/radeonsi/si_state.c
index 026aded..803a021 100644
--- a/src/gallium/drivers/radeonsi/si_state.c
+++ b/src/gallium/drivers/radeonsi/si_state.c
@@ -3045,20 +3045,24 @@ si_create_sampler_view_custom(struct pipe_context *ctx,
 		case PIPE_FORMAT_X24S8_UINT:
 		case PIPE_FORMAT_S8X24_UINT:
 		case PIPE_FORMAT_X32_S8X24_UINT:
 			pipe_format = PIPE_FORMAT_S8_UINT;
 			surflevel = tmp->surface.stencil_level;
 			break;
 		default:;
 		}
 	}
 
+	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,
 				   state->u.tex.first_layer, last_layer,
 				   width, height, depth,
 				   view->state, view->fmask_state);
 
 	view->base_level_info = &surflevel[base_level];
 	view->base_level = base_level;
 	view->block_width = util_format_get_blockwidth(pipe_format);
-- 
2.7.4



More information about the mesa-dev mailing list