pan/mod: Allow testing if a modifier is optimal

Testing if a modifier is supported for a set of image properties is not
enough when we're supposed to pick the best modifier among a selection
of modifiers. Extend the image/mod logic to report if a modifier is
optimal when supported.

This forces us to pass extra usage information through a new
pan_image_usage object. When this usage info is missing (NULL), the
mod layer assumes a set of usage that would make the modifier optimal.

Note that the usage info also allows us to reject a few more cases
that would otherwise need to be checked by the frontend.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Eric R. Smith <eric.smith@collabora.com>
Reviewed-by: Christoph Pillmayer <christoph.pillmayer@arm.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/37158>
This commit is contained in:
Boris Brezillon 2025-08-29 10:56:51 +02:00 committed by Marge Bot
parent a4547fd4b1
commit bff39c4f25
4 changed files with 148 additions and 36 deletions

View file

@ -252,9 +252,24 @@ bool pan_image_layout_init(
unsigned arch, struct pan_image *image, unsigned plane_idx,
const struct pan_image_layout_constraints *explicit_layout_constraints);
static inline bool
struct pan_image_usage {
/* PAN_BIND_xxx flags. */
uint32_t bind;
/* Image filled directly from the CPU. */
bool host_copy;
/* Image frequently updated with host data. */
bool frequent_host_updates;
/* Scanout image. */
bool scanout;
};
static inline enum pan_mod_support
pan_image_test_props(const struct pan_kmod_dev_props *dprops,
const struct pan_image_props *iprops)
const struct pan_image_props *iprops,
const struct pan_image_usage *iusage)
{
const unsigned arch = pan_arch(dprops->gpu_id);
struct pan_image image = {
@ -263,10 +278,12 @@ pan_image_test_props(const struct pan_kmod_dev_props *dprops,
};
if (!image.mod_handler)
return false;
return PAN_MOD_NOT_SUPPORTED;
if (!image.mod_handler->test_props(dprops, &image.props))
return false;
enum pan_mod_support ret =
image.mod_handler->test_props(dprops, &image.props, iusage);
if (ret == PAN_MOD_NOT_SUPPORTED)
return ret;
/* Now make sure the layout can be properly initialized on all planes. */
uint32_t plane_count = util_format_get_num_planes(image.props.format);
@ -277,10 +294,10 @@ pan_image_test_props(const struct pan_kmod_dev_props *dprops,
image.planes[p] = &plane;
if (!pan_image_layout_init(arch, &image, p, NULL))
return false;
return PAN_MOD_NOT_SUPPORTED;
}
return true;
return ret;
}
static inline bool
@ -304,10 +321,9 @@ pan_image_test_modifier_with_format(const struct pan_kmod_dev_props *dprops,
.array_size = 1,
};
return pan_image_test_props(dprops, &iprops);
return pan_image_test_props(dprops, &iprops, NULL) != PAN_MOD_NOT_SUPPORTED;
}
#ifdef __cplusplus
} /* extern C */
#endif

View file

@ -171,50 +171,99 @@ pan_mod_afbc_init_slice_layout(
return true;
}
static bool
static enum pan_mod_support
pan_mod_afbc_test_props(const struct pan_kmod_dev_props *dprops,
const struct pan_image_props *iprops)
const struct pan_image_props *iprops,
const struct pan_image_usage *iusage)
{
/* No image store. */
if (iusage && iusage->bind & PAN_BIND_STORAGE_IMAGE)
return PAN_MOD_NOT_SUPPORTED;
/* AFBC not supported. */
if (!pan_query_afbc(dprops))
return false;
return PAN_MOD_NOT_SUPPORTED;
/* Check if the format is supported first. */
if (!pan_afbc_supports_format(PAN_ARCH, iprops->format))
return false;
return PAN_MOD_NOT_SUPPORTED;
/* AFBC can't do multisampling. */
if (iprops->nr_samples > 1)
return false;
return PAN_MOD_NOT_SUPPORTED;
/* AFBC(2D) or AFBC(3D) on v7+ only. */
if ((iprops->dim == MALI_TEXTURE_DIMENSION_3D && PAN_ARCH < 7) ||
iprops->dim != MALI_TEXTURE_DIMENSION_2D)
return false;
return PAN_MOD_NOT_SUPPORTED;
unsigned plane_count = util_format_get_num_planes(iprops->format);
const struct util_format_description *fdesc =
util_format_description(iprops->format);
/* ZS buffer descriptors can't pass split/wide/YTR modifiers. */
if (iusage && (iusage->bind & PAN_BIND_DEPTH_STENCIL) &&
(pan_afbc_superblock_width(iprops->modifier) != 16 ||
(iprops->modifier & (AFBC_FORMAT_MOD_SPLIT | AFBC_FORMAT_MOD_YTR))))
return PAN_MOD_NOT_SUPPORTED;
/* YTR is only useful on RGB formats. */
if ((iprops->modifier & AFBC_FORMAT_MOD_YTR) &&
(pan_format_is_yuv(iprops->format) || fdesc->nr_channels < 3))
return false;
return PAN_MOD_NOT_SUPPORTED;
/* Make sure all planes support split mode. */
if ((iprops->modifier & AFBC_FORMAT_MOD_SPLIT)) {
for (unsigned p = 0; p < plane_count; p++) {
if (!pan_afbc_can_split(PAN_ARCH, iprops->format, iprops->modifier, p))
return false;
return PAN_MOD_NOT_SUPPORTED;
}
}
/* Make sure tiled mode is supported. */
if ((iprops->modifier & AFBC_FORMAT_MOD_TILED) &&
!pan_afbc_can_tile(PAN_ARCH))
return false;
return PAN_MOD_NOT_SUPPORTED;
return true;
/* For one tile, AFBC is a loss compared to u-interleaved */
if (iprops->extent_px.width <= 16 && iprops->extent_px.height <= 16)
return PAN_MOD_NOT_OPTIMAL;
/* Reserve 32x8 tiles for scanout buffers. */
if (iusage && !iusage->scanout &&
pan_afbc_superblock_width(iprops->modifier) != 16)
return PAN_MOD_NOT_OPTIMAL;
/* Prefer YTR when available. */
if (pan_afbc_can_ytr(iprops->format) &&
!(iprops->modifier & AFBC_FORMAT_MOD_YTR))
return PAN_MOD_NOT_OPTIMAL;
if (iprops->modifier & (AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SC))
return PAN_MOD_NOT_SUPPORTED;
bool is_tiled = iprops->modifier & AFBC_FORMAT_MOD_TILED;
bool can_tile = pan_afbc_can_tile(PAN_ARCH);
if (is_tiled && !can_tile)
return PAN_MOD_NOT_SUPPORTED;
/* Prefer tiled headers when the image is big enough. */
bool should_tile =
iprops->extent_px.width >= 128 && iprops->extent_px.height >= 128;
if (is_tiled != should_tile)
return PAN_MOD_NOT_OPTIMAL;
/* Packing/unpacking AFBC payload requires a COMPUTE job which we'd rather
* avoid.
*/
if (iusage &&
(iusage->bind & (PAN_BIND_DEPTH_STENCIL | PAN_BIND_RENDER_TARGET)) &&
!(iprops->modifier & AFBC_FORMAT_MOD_SPARSE))
return PAN_MOD_NOT_OPTIMAL;
return PAN_MOD_OPTIMAL;
}
#define pan_mod_afbc_emit_tex_payload_entry \
@ -230,19 +279,40 @@ pan_mod_afrc_match(uint64_t mod)
return drm_is_afrc(mod);
}
static bool
static enum pan_mod_support
pan_mod_afrc_test_props(const struct pan_kmod_dev_props *dprops,
const struct pan_image_props *iprops)
const struct pan_image_props *iprops,
const struct pan_image_usage *iusage)
{
/* AFRC not supported. */
if (!pan_query_afrc(dprops))
return false;
return PAN_MOD_NOT_SUPPORTED;
/* Format not AFRC-able. */
if (!pan_afrc_supports_format(iprops->format))
return false;
return PAN_MOD_NOT_SUPPORTED;
return true;
/* AFRC does not support layered multisampling. */
if (iprops->nr_samples > 1)
return PAN_MOD_NOT_SUPPORTED;
/* No image store. */
if (iusage && iusage->bind & PAN_BIND_STORAGE_IMAGE)
return PAN_MOD_NOT_SUPPORTED;
/* We can't write to an AFRC resource directly. */
if (iusage && iusage->host_copy)
return PAN_MOD_NOT_SUPPORTED;
/* Host updates require an extra blit which we would rather avoid. */
if (iusage && iusage->frequent_host_updates)
return PAN_MOD_NOT_OPTIMAL;
/* There's nothing preventing 1D AFRC, but it's pointless. */
if (iprops->dim == MALI_TEXTURE_DIMENSION_1D)
return PAN_MOD_NOT_OPTIMAL;
return PAN_MOD_OPTIMAL;
}
static uint32_t
@ -350,17 +420,26 @@ pan_mod_u_tiled_match(uint64_t mod)
return mod == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED;
}
static bool
static enum pan_mod_support
pan_mod_u_tiled_test_props(const struct pan_kmod_dev_props *dprops,
const struct pan_image_props *iprops)
const struct pan_image_props *iprops,
const struct pan_image_usage *iusage)
{
assert(GENX(pan_format_from_pipe_format)(iprops->format)->hw);
/* YUV not supported. */
if (pan_format_is_yuv(iprops->format))
return false;
return PAN_MOD_NOT_SUPPORTED;
return true;
/* The purpose of tiling is improving locality in both X- and
* Y-directions. If there is only a single pixel in either direction,
* tiling does not make sense; using a linear layout instead is optimal
* for both memory usage and performance.
*/
if (MIN2(iprops->extent_px.width, iprops->extent_px.height) < 2)
return PAN_MOD_NOT_OPTIMAL;
return PAN_MOD_OPTIMAL;
}
static uint32_t
@ -479,9 +558,10 @@ pan_mod_linear_match(uint64_t mod)
return mod == DRM_FORMAT_MOD_LINEAR;
}
static bool
static enum pan_mod_support
pan_mod_linear_test_props(const struct pan_kmod_dev_props *dprops,
const struct pan_image_props *iprops)
const struct pan_image_props *iprops,
const struct pan_image_usage *iusage)
{
assert(GENX(pan_format_from_pipe_format)(iprops->format)->hw);
@ -489,10 +569,14 @@ pan_mod_linear_test_props(const struct pan_kmod_dev_props *dprops,
/* AFBC-only formats. */
case PIPE_FORMAT_R8G8B8_420_UNORM_PACKED:
case PIPE_FORMAT_R10G10B10_420_UNORM_PACKED:
return false;
return PAN_MOD_NOT_SUPPORTED;
default:
return true;
/* We assume that all "better" mods have been tested before linear, and
* declare it as optimal so it's always picked when tested, unless it's
* not supported.
*/
return PAN_MOD_OPTIMAL;
}
}

View file

@ -18,15 +18,27 @@ extern "C" {
struct pan_fb_info;
struct pan_image;
struct pan_image_view;
struct pan_image_usage;
struct pan_kmod_dev_props;
struct pan_mod_handler;
enum pan_mod_support {
PAN_MOD_NOT_SUPPORTED = 0,
PAN_MOD_NOT_OPTIMAL,
PAN_MOD_OPTIMAL,
};
struct pan_mod_handler {
bool (*match)(uint64_t mod);
/* Used to check if a set of image properties is valid. */
bool (*test_props)(const struct pan_kmod_dev_props *dprops,
const struct pan_image_props *iprops);
/* Used to check if a set of image properties is valid. Passing a NULL iusage
* is valid and means "optimal set of usage for this mod". This implies
* that some non-supported cases can't be detected or can be reported as
* optimal when specific usage flags would report it non-optimal.
*/
enum pan_mod_support (*test_props)(const struct pan_kmod_dev_props *dprops,
const struct pan_image_props *iprops,
const struct pan_image_usage *iusage);
bool (*init_slice_layout)(
const struct pan_image_props *props, unsigned plane_idx,

View file

@ -175,7 +175,7 @@ panvk_image_can_use_mod(struct panvk_image *image, uint64_t mod)
.depth = image->vk.extent.depth,
};
if (!pan_image_test_props(&phys_dev->kmod.props, &iprops))
if (!pan_image_test_props(&phys_dev->kmod.props, &iprops, NULL))
return false;
}