From: Marek Olšák <marek.ol...@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 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev