panfrost: Make the MTK detiling more robust

There are several problems in the current logic:

- it doesn't account for the WSI stride, and just assumes things are
  tightly packed and the resource extent is aligned on a tile
- the format re-interpretation done in panfrost_mtk_detile_compute()
  where the view format and the image format have a different block size
  is not supposed to be supported, and that's something we'd like
  to enforce now
- we write to textures that may have no descriptors bound to. This works
  thanks to the robustness behind image stores/loads, but we'd probably
  rather discard a bunch of instructions when Y/UV planes are copied
  separately
- the linear to tiled coordinates conversion can be simplified by using
  a combination of local_invocation_id+workgroup_id instead of the
  global_invocation_id

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Mary Guillemard <mary.guillemard@collabora.com>
Tested-by: Eric R. Smith <eric.smith@collabora.com>
Reviewed-by: Eric R. Smith <eric.smith@collabora.com>
Acked-by: Erik Faye-Lund <erik.faye-lund@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/35317>
This commit is contained in:
Boris Brezillon 2025-06-03 11:57:13 +02:00 committed by Marge Bot
parent a87805fdd8
commit eb476fd2c6
5 changed files with 285 additions and 130 deletions

View file

@ -3652,10 +3652,6 @@ panfrost_mtk_detile_compute(struct panfrost_context *ctx, struct pipe_blit_info
unsigned width = info->src.box.width;
unsigned height = info->src.box.height;
unsigned src_stride =
pan_resource(y_src)->plane.layout.slices[0].row_stride_B;
unsigned dst_stride =
pan_resource(y_dst)->plane.layout.slices[0].row_stride_B;
/* 4 images: y_src, uv_src, y_dst, uv_dst */
struct pipe_image_view image[4] = { 0 };
@ -3673,6 +3669,43 @@ panfrost_mtk_detile_compute(struct panfrost_context *ctx, struct pipe_blit_info
y_src = y_dst = NULL;
}
}
/* We're not supposed to create views with a format whose size doesn't match
* the image format. */
struct panfrost_resource y_src_save;
struct panfrost_resource uv_src_save;
struct panfrost_resource y_dst_save;
struct panfrost_resource uv_dst_save;
panfrost_resource_change_format(pan_resource(y_src),
PIPE_FORMAT_R8G8B8A8_UINT, &y_src_save);
panfrost_resource_change_format(pan_resource(uv_src),
PIPE_FORMAT_R8G8B8A8_UINT, &uv_src_save);
panfrost_resource_change_format(pan_resource(y_dst),
PIPE_FORMAT_R8G8B8A8_UINT, &y_dst_save);
panfrost_resource_change_format(pan_resource(uv_dst),
PIPE_FORMAT_R8G8B8A8_UINT, &uv_dst_save);
struct panfrost_mtk_detile_info consts = {
.height = height,
/* The copy width is expressed for an R8_UNORM resource, but we
* changed the format into RGBA8_UINT, so we need to adjust the width if
* we want the shader-side bound check to do its job. */
.width = width / 4,
};
if (y_src) {
consts.src_y_row_stride_tl =
pan_resource(y_src)->image.props.extent_px.height /
DIV_ROUND_UP(y_src->height0, 32);
}
if (uv_src) {
consts.src_uv_row_stride_tl =
pan_resource(uv_src)->image.props.extent_px.height /
DIV_ROUND_UP(uv_src->height0, 16);
}
image[0].resource = y_src;
image[0].format = PIPE_FORMAT_R8G8B8A8_UINT;
image[0].shader_access = image[0].access = PIPE_IMAGE_ACCESS_READ;
@ -3701,12 +3734,6 @@ panfrost_mtk_detile_compute(struct panfrost_context *ctx, struct pipe_blit_info
image[3].u.tex.first_layer = 0;
image[3].u.tex.last_layer = uv_dst ? (unsigned)(uv_dst->array_size - 1) : 0;
struct panfrost_mtk_detile_info consts = {
.tiles_per_stride = src_stride >> 4,
.src_width = width,
.src_height = height,
.dst_stride = dst_stride,
};
panfrost_flush_all_batches(ctx, "mtk_detile pre-barrier");
struct panfrost_batch *batch = panfrost_get_batch_for_fbo(ctx);
@ -3714,20 +3741,20 @@ panfrost_mtk_detile_compute(struct panfrost_context *ctx, struct pipe_blit_info
/* launch the compute shader */
struct pan_mod_convert_shader_data *shader =
panfrost_get_mtk_detile_shader(ctx);
panfrost_get_mtk_detile_shader(ctx, y_src != NULL, uv_src != NULL);
struct pipe_constant_buffer cbuf = {
.buffer_size = sizeof(consts),
.user_buffer = &consts};
struct pipe_grid_info grid_info = {
.block[0] = 4,
.last_block[0] = (width/4) % 4,
.last_block[0] = (width / 4) % 4,
.block[1] = 16,
.last_block[1] = height % 16,
.last_block[1] = (height / 2) % 16,
.block[2] = 1,
.last_block[2] = 0,
.grid[0] = DIV_ROUND_UP(width/4, 4),
.grid[1] = DIV_ROUND_UP(height, 16),
.grid[0] = DIV_ROUND_UP(width / 4, 4),
.grid[1] = DIV_ROUND_UP(height / 2, 16),
.grid[2] = 1,
};
@ -3745,6 +3772,11 @@ panfrost_mtk_detile_compute(struct panfrost_context *ctx, struct pipe_blit_info
pipe->bind_compute_state(pipe, saved_cso);
pipe->set_constant_buffer(pipe, PIPE_SHADER_COMPUTE, 0, true, &saved_const);
panfrost_resource_restore_format(pan_resource(y_src), &y_src_save);
panfrost_resource_restore_format(pan_resource(uv_src), &uv_src_save);
panfrost_resource_restore_format(pan_resource(y_dst), &y_dst_save);
panfrost_resource_restore_format(pan_resource(uv_dst), &uv_dst_save);
}
static void *

View file

@ -285,55 +285,21 @@ panfrost_create_afbc_pack_shader(struct panfrost_screen *screen,
#define panfrost_mtk_detile_get_info_field(b, field) \
panfrost_mtk_get_info_field(detile, b, field)
static nir_def *
pan_mtk_tiled_from_linear(nir_builder *b, nir_def *linear, nir_def *tiles_per_stride, nir_def *width)
static void
copy_y_uv_texel(nir_builder *b, unsigned src_img, nir_def *src_coords,
unsigned dst_img, nir_def *dst_coords)
{
nir_def *tiled;
/* uvec2 tlc = uvec2(linear) >> uvec2(2u, 5u) */
nir_def *tlc = nir_ushr(b, linear,
nir_imm_ivec2(b, 2, 5));
nir_def *sample = nir_imm_int(b, 0);
nir_def *lod = nir_imm_int(b, 0);
/* uvec2 txc = uvec2(linear) & uvec2(3u, 31u) */
nir_def *txc = nir_iand(b, linear,
nir_imm_ivec2(b, 3, 31));
/* uint tlo = tlc.y * tiles_per_stride + tlc.x */
nir_def *tlo = nir_iadd(b,
nir_imul(b,
nir_channel(b, tlc, 1),
tiles_per_stride),
nir_channel(b, tlc, 0));
nir_def *txcx = nir_channel(b, txc, 0);
nir_def *txcy = nir_channel(b, txc, 1);
nir_def *txcytmp = nir_vec2(b, txcy,
nir_ushr_imm(b, txcy, 1));
/* txo = (uvec2(txc.y, txc.y >> 1) << uvec2(2u)) | txc.xx */
nir_def *txo = nir_ior(b,
nir_ishl_imm(b, txcytmp, 2),
nir_vec2(b, txcx, txcx));
/* uvec2 off = (uvec2(tlo) << uvec2(7u, 6u)) | txo */
nir_def *off = nir_ior(b,
nir_ishl(b,
nir_vec2(b, tlo, tlo),
nir_imm_ivec2(b, 7, 6)),
txo);
/* convert to 2D coord
* tiled.xy = off % (width / 4, width / 4)
* tiled.zw = off / (width / 4, width / 4) */
nir_def *width4 = nir_ishl_imm(b, tiles_per_stride, 2);
width4 = nir_vec2(b, width4, width4);
nir_def *tiled_xy = nir_umod(b, off, width4);
nir_def *tiled_zw = nir_udiv(b, off, width4);
tiled = nir_vec4(b,
nir_channel(b, tiled_xy, 0),
nir_channel(b, tiled_xy, 1),
nir_channel(b, tiled_zw, 0),
nir_channel(b, tiled_zw, 1));
return tiled;
nir_def *val = nir_image_load(
b, 4, 32, nir_imm_int(b, src_img), src_coords, sample, lod,
.access = ACCESS_NON_WRITEABLE, .image_dim = GLSL_SAMPLER_DIM_2D,
.image_array = false, .dest_type = nir_type_uint32);
nir_image_store(b, nir_imm_int(b, dst_img), dst_coords, sample, val, lod,
.access = ACCESS_NON_READABLE,
.image_dim = GLSL_SAMPLER_DIM_2D, .image_array = false,
.src_type = nir_type_uint32);
}
static nir_shader *
@ -348,6 +314,36 @@ panfrost_create_mtk_tiled_detile_shader(
b.shader->info.workgroup_size[0] = 4;
b.shader->info.workgroup_size[1] = 16;
b.shader->info.workgroup_size[2] = 1;
nir_def *intra_tile_coords = nir_trim_vector(&b, nir_load_local_invocation_id(&b), 2);
nir_def *wg_id = nir_trim_vector(&b, nir_load_workgroup_id(&b), 2);
nir_def *uv_linear_coords = nir_pad_vector_imm_int(
&b,
nir_iadd(&b, nir_imul(&b, wg_id, nir_imm_ivec2(&b, 4, 16)),
intra_tile_coords),
0, 4);
nir_def *y_linear_coords =
nir_ishl(&b, uv_linear_coords, nir_imm_ivec4(&b, 0, 1, 0, 0));
nir_def *src_y_row_stride_tl =
panfrost_mtk_detile_get_info_field(&b, src_y_row_stride_tl);
nir_def *src_uv_row_stride_tl =
panfrost_mtk_detile_get_info_field(&b, src_uv_row_stride_tl);
nir_def *dst_extent =
nir_vec2(&b, panfrost_mtk_detile_get_info_field(&b, width),
panfrost_mtk_detile_get_info_field(&b, height));
nir_def *uv_tiled_coords = nir_vec4(
&b,
nir_iadd(&b, nir_imul_imm(&b, nir_channel(&b, intra_tile_coords, 1), 4),
nir_channel(&b, intra_tile_coords, 0)),
nir_iadd(&b, nir_imul(&b, nir_channel(&b, wg_id, 1), src_uv_row_stride_tl),
nir_channel(&b, wg_id, 0)),
nir_imm_int(&b, 0), nir_imm_int(&b, 0));
nir_def *y_tiled_coords = nir_vec4(
&b,
nir_iadd(&b, nir_imul_imm(&b, nir_channel(&b, intra_tile_coords, 1), 8),
nir_channel(&b, intra_tile_coords, 0)),
nir_iadd(&b, nir_imul(&b, nir_channel(&b, wg_id, 1), src_y_row_stride_tl),
nir_channel(&b, wg_id, 0)),
nir_imm_int(&b, 0), nir_imm_int(&b, 0));
const struct glsl_type *image_type =
glsl_image_type(GLSL_SAMPLER_DIM_2D, /*is_array*/ false, GLSL_TYPE_UINT);
@ -377,70 +373,35 @@ panfrost_create_mtk_tiled_detile_shader(
uv_linear->data.image.format = PIPE_FORMAT_R8G8B8A8_UINT;
BITSET_SET(b.shader->info.images_used, 3);
nir_def *linear = nir_load_global_invocation_id(&b, 32);
nir_def *tiles_per_stride =
panfrost_mtk_detile_get_info_field(&b, tiles_per_stride);
nir_def *src_width = panfrost_mtk_detile_get_info_field(&b, src_width);
nir_def *zero = nir_imm_int(&b, 0);
nir_def *coord = nir_vec2(&b,
nir_channel(&b, linear, 0),
nir_channel(&b, linear, 1));
nir_def *tiled = pan_mtk_tiled_from_linear(&b, coord, tiles_per_stride, src_width);
nir_def *tiled_xz = nir_vec4(&b, nir_channel(&b, tiled, 0),
nir_channel(&b, tiled, 2), zero, zero);
nir_def *tiled_yw = nir_vec4(&b, nir_channel(&b, tiled, 1),
nir_channel(&b, tiled, 3), zero, zero);
nir_def *yval = nir_image_load(&b, 4, 32, zero, tiled_xz,
zero /* sample */, zero /* lod */,
.access = ACCESS_NON_WRITEABLE,
.image_dim = GLSL_SAMPLER_DIM_2D,
.image_array = false,
.dest_type = nir_type_uint32);
nir_def *uvval;
nir_def *dst_y_coord = nir_vec4(&b,
nir_channel(&b, coord, 0),
nir_channel(&b, coord, 1),
zero, zero);
/* store Y data */
nir_def *img_deref_st_y = nir_imm_int(&b, 2);
nir_image_store(&b, img_deref_st_y, dst_y_coord, zero /* sample */,
yval, zero /* lod */,
.access = ACCESS_NON_READABLE,
.image_dim = GLSL_SAMPLER_DIM_2D,
.image_array = false, .src_type = nir_type_uint32);
/* store UV data */
nir_def *odd_even_line = nir_iand_imm(&b,
nir_channel(&b, dst_y_coord, 1),
1);
nir_push_if(&b, nir_ieq_imm(&b, odd_even_line, 0));
nir_def *in_bounds = nir_ball(&b, nir_ilt(&b, y_linear_coords, dst_extent));
nir_push_if(&b, in_bounds);
{
if (tint_yuv) {
/* use just blue for chroma */
uvval = nir_imm_ivec4(&b, 0xc0, 0x80, 0xc0, 0x80);
} else {
nir_def *img_deref_uv = nir_imm_int(&b, 1);
uvval = nir_image_load(&b, 4, 32, img_deref_uv, tiled_yw,
zero /* sample */, zero /* lod */,
.access = ACCESS_NON_WRITEABLE,
.image_dim = GLSL_SAMPLER_DIM_2D,
.image_array = false,
.dest_type = nir_type_uint32);
if (key->mtk_tiled.has_y)
copy_y_uv_texel(&b, 0, y_tiled_coords, 2, y_linear_coords);
if (key->mtk_tiled.has_uv) {
if (!tint_yuv) {
copy_y_uv_texel(&b, 1, uv_tiled_coords, 3, uv_linear_coords);
} else {
/* use just blue for chroma */
nir_def *val = nir_imm_ivec4(&b, 0xc0, 0x80, 0xc0, 0x80);
nir_def *sample = nir_imm_int(&b, 0);
nir_def *lod = nir_imm_int(&b, 0);
nir_image_store(&b, nir_imm_int(&b, 3), uv_linear_coords, sample,
val, lod, .access = ACCESS_NON_READABLE,
.image_dim = GLSL_SAMPLER_DIM_2D,
.image_array = false, .src_type = nir_type_uint32);
}
}
/* Next line of Y components, UV is vertically subsampled. */
if (key->mtk_tiled.has_y) {
y_linear_coords =
nir_iadd(&b, y_linear_coords, nir_imm_ivec2(&b, 0, 1));
y_tiled_coords = nir_iadd(&b, y_tiled_coords, nir_imm_ivec2(&b, 4, 0));
copy_y_uv_texel(&b, 0, y_tiled_coords, 2, y_linear_coords);
}
nir_def *dst_uv_coord = nir_ishr(&b, dst_y_coord,
nir_imm_ivec4(&b, 0, 1, 0, 0));
nir_def *img_deref_st_uv = nir_imm_int(&b, 3);
nir_image_store(&b, img_deref_st_uv, dst_uv_coord, zero /* sample */,
uvval, zero /* lod */,
.access = ACCESS_NON_READABLE,
.image_dim = GLSL_SAMPLER_DIM_2D,
.image_array = false, .src_type = nir_type_uint32);
}
nir_pop_if(&b, NULL);
@ -510,10 +471,15 @@ panfrost_get_afbc_pack_shaders(struct panfrost_context *ctx,
}
struct pan_mod_convert_shader_data *
panfrost_get_mtk_detile_shader(struct panfrost_context *ctx)
panfrost_get_mtk_detile_shader(struct panfrost_context *ctx, bool has_y,
bool has_uv)
{
struct pan_mod_convert_shader_key key = {
.mod = DRM_FORMAT_MOD_MTK_16L_32S_TILE,
.mtk_tiled = {
.has_y = has_y,
.has_uv = has_uv,
},
};
return get_mod_convert_shaders(ctx, &key);

View file

@ -41,6 +41,11 @@ struct pan_mod_convert_shader_key {
unsigned bpp;
unsigned align;
} afbc;
struct {
unsigned has_y : 1;
unsigned has_uv : 1;
unsigned unused : 30;
} mtk_tiled;
};
};
@ -83,10 +88,10 @@ struct panfrost_afbc_pack_info {
} PACKED;
struct panfrost_mtk_detile_info {
uint32_t tiles_per_stride;
uint32_t src_width;
uint32_t src_height;
uint32_t dst_stride;
uint32_t src_y_row_stride_tl;
uint32_t src_uv_row_stride_tl;
uint32_t width;
uint32_t height;
} PACKED;
void panfrost_afbc_context_init(struct panfrost_context *ctx);
@ -97,7 +102,8 @@ panfrost_get_afbc_pack_shaders(struct panfrost_context *ctx,
struct panfrost_resource *rsrc, unsigned align);
struct pan_mod_convert_shader_data *
panfrost_get_mtk_detile_shader(struct panfrost_context *ctx);
panfrost_get_mtk_detile_shader(struct panfrost_context *ctx, bool has_y,
bool has_uv);
#define drm_is_mtk_tiled(mod) \
((mod >> 52) == (0 | (DRM_FORMAT_MOD_VENDOR_MTK << 4)))

View file

@ -132,6 +132,54 @@ panfrost_resource_init_image(struct panfrost_resource *rsc,
plane->image = rsc->image;
}
static bool
adjust_mtk_tiled_props(struct panfrost_resource *rsc,
struct pan_image_props *iprops, unsigned plane_idx,
struct pan_image_layout_constraints *explicit_layout)
{
bool is_uv_plane =
iprops->format == PIPE_FORMAT_R8G8_UNORM ||
(iprops->format == PIPE_FORMAT_R8_G8B8_420_UNORM && plane_idx > 0);
unsigned tile_w_px, tile_h_px, blksz_B;
if (is_uv_plane) {
tile_w_px = 8;
tile_h_px = 16;
blksz_B = 2;
iprops->format = PIPE_FORMAT_R8G8_UNORM;
} else {
tile_w_px = 16;
tile_h_px = 32;
blksz_B = 1;
iprops->format = PIPE_FORMAT_R8_UNORM;
}
/* SW detiling on MTK_TILED resources. This forces us to treat such
* resources as linear images with:
* width = tile_width * tile_height
* height = (wsi_row_stride / (tile_width * blksize)) * (height /
* tile_height)
*/
iprops->extent_px.width = tile_w_px * tile_h_px;
iprops->extent_px.height =
(explicit_layout->wsi_row_pitch_B / (blksz_B * tile_w_px)) *
DIV_ROUND_UP(rsc->base.height0, tile_h_px);
/* Reject the import if the pitch is not aligned on a tile or if it's not
* covering the resource width. */
unsigned min_row_pitch_B = rsc->base.width0 * blksz_B;
unsigned row_pitch_align_req_B = blksz_B * tile_w_px;
if (explicit_layout->strict &&
(explicit_layout->wsi_row_pitch_B % row_pitch_align_req_B != 0 ||
explicit_layout->wsi_row_pitch_B < min_row_pitch_B))
return false;
/* Now adjust the row pitch. */
explicit_layout->wsi_row_pitch_B = iprops->extent_px.width * blksz_B;
return true;
}
static struct pipe_resource *
panfrost_resource_from_handle(struct pipe_screen *pscreen,
const struct pipe_resource *templat,
@ -190,6 +238,14 @@ panfrost_resource_from_handle(struct pipe_screen *pscreen,
.nr_samples = MAX2(prsc->nr_samples, 1),
.nr_slices = 1,
};
if (drm_is_mtk_tiled(mod) &&
!adjust_mtk_tiled_props(rsc, &iprops, whandle->plane,
&explicit_layout)) {
FREE(rsc);
return NULL;
}
unsigned format_plane =
util_format_get_num_planes(iprops.format) > 1 ? whandle->plane : 0;
@ -266,6 +322,26 @@ panfrost_resource_get_handle(struct pipe_screen *pscreen,
&rsrc->image.planes[handle->plane]->layout, 0);
handle->offset =
pan_image_get_wsi_offset(&rsrc->image.planes[handle->plane]->layout, 0);
/* SW detiling on MTK_TILED resources. This forces us to treat such
* resources as linear images with:
* width = tile_width * tile_height
* height = (wsi_row_stride / (tile_width * blksize)) * (height / tile_height)
*
* We need to extract the original WSI row pitch from this.
*/
if (drm_is_mtk_tiled(rsrc->modifier)) {
bool subsamp = handle->plane > 0 ||
rsrc->image.props.format == PIPE_FORMAT_R8G8_UNORM;
unsigned blksz_B = subsamp ? 2 : 1;
unsigned tile_w_px = 16 / (subsamp ? 2 : 1);
unsigned tile_h_px = 32 / (subsamp ? 2 : 1);
unsigned row_stride_tl = rsrc->image.props.extent_px.height /
DIV_ROUND_UP(rsrc->base.height0, tile_h_px);
handle->stride = row_stride_tl * tile_w_px * blksz_B;
}
return true;
}
@ -978,6 +1054,72 @@ panfrost_resource_create_with_modifiers(struct pipe_screen *screen,
return panfrost_resource_create(screen, template);
}
void
panfrost_resource_change_format(struct panfrost_resource *rsrc,
enum pipe_format new_format,
struct panfrost_resource *save)
{
if (!rsrc)
return;
assert(rsrc->image.props.modifier == DRM_FORMAT_MOD_LINEAR);
assert(util_format_get_num_planes(new_format) == 1);
assert(util_format_get_blockwidth(new_format) == 1 &&
util_format_get_blockheight(new_format) == 1);
assert(util_format_get_blockwidth(rsrc->image.props.format) == 1 &&
util_format_get_blockheight(rsrc->image.props.format) == 1);
if (new_format == rsrc->image.props.format)
return;
*save = *rsrc;
unsigned old_res_plane_idx = pan_resource_plane_index(rsrc);
enum pipe_format old_format =
util_format_get_plane_format(rsrc->image.props.format, old_res_plane_idx);
unsigned old_width =
util_format_get_plane_width(rsrc->image.props.format, old_res_plane_idx,
rsrc->image.props.extent_px.width);
unsigned old_fmt_blksize = util_format_get_blocksize(old_format);
unsigned new_fmt_blksize = util_format_get_blocksize(new_format);
if (old_fmt_blksize != new_fmt_blksize) {
assert((old_fmt_blksize * rsrc->base.width0) % new_fmt_blksize == 0);
rsrc->base.width0 =
(old_fmt_blksize * rsrc->base.width0) / new_fmt_blksize;
rsrc->image.props.extent_px.width =
(old_fmt_blksize * old_width) /
new_fmt_blksize;
rsrc->image.props.extent_px.height =
util_format_get_plane_height(rsrc->image.props.format, old_res_plane_idx,
rsrc->image.props.extent_px.height);
}
rsrc->base.next = NULL;
rsrc->base.format = new_format;
rsrc->image.props.format = new_format;
rsrc->image.planes[0] = &rsrc->plane;
rsrc->image.planes[1] = NULL;
rsrc->image.planes[2] = NULL;
}
void
panfrost_resource_restore_format(struct panfrost_resource *rsrc,
const struct panfrost_resource *saved)
{
if (!rsrc)
return;
rsrc->base.next = saved->base.next;
memcpy(rsrc->image.planes, saved->image.planes, sizeof(rsrc->image.planes));
rsrc->base.format = saved->base.format;
rsrc->image.props.format = saved->image.props.format;
rsrc->base.width0 = saved->base.width0;
rsrc->image.props.extent_px.width = saved->image.props.extent_px.width;
rsrc->image.props.extent_px.height = saved->image.props.extent_px.height;
}
static void
panfrost_resource_destroy(struct pipe_screen *screen, struct pipe_resource *pt)
{

View file

@ -197,6 +197,15 @@ panfrost_translate_texture_dimension(enum pipe_texture_target t)
}
}
void
panfrost_resource_change_format(struct panfrost_resource *rsrc,
enum pipe_format new_format,
struct panfrost_resource *save);
void
panfrost_resource_restore_format(struct panfrost_resource *rsrc,
const struct panfrost_resource *saved);
bool panfrost_should_pack_afbc(struct panfrost_device *dev,
const struct panfrost_resource *rsrc);