From bff39c4f25e8812d02466a1d2c0c278963f00436 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 29 Aug 2025 10:56:51 +0200 Subject: [PATCH] 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 Reviewed-by: Eric R. Smith Reviewed-by: Christoph Pillmayer Part-of: --- src/panfrost/lib/pan_image.h | 34 +++++--- src/panfrost/lib/pan_mod.c | 130 ++++++++++++++++++++++++------ src/panfrost/lib/pan_mod.h | 18 ++++- src/panfrost/vulkan/panvk_image.c | 2 +- 4 files changed, 148 insertions(+), 36 deletions(-) diff --git a/src/panfrost/lib/pan_image.h b/src/panfrost/lib/pan_image.h index 2faf094b444..e1a8add2aba 100644 --- a/src/panfrost/lib/pan_image.h +++ b/src/panfrost/lib/pan_image.h @@ -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 diff --git a/src/panfrost/lib/pan_mod.c b/src/panfrost/lib/pan_mod.c index bfb1f6b1939..4eb4906d435 100644 --- a/src/panfrost/lib/pan_mod.c +++ b/src/panfrost/lib/pan_mod.c @@ -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; } } diff --git a/src/panfrost/lib/pan_mod.h b/src/panfrost/lib/pan_mod.h index 307da90c398..ce1ab08f9f8 100644 --- a/src/panfrost/lib/pan_mod.h +++ b/src/panfrost/lib/pan_mod.h @@ -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, diff --git a/src/panfrost/vulkan/panvk_image.c b/src/panfrost/vulkan/panvk_image.c index b28f752c798..2ae46f008cd 100644 --- a/src/panfrost/vulkan/panvk_image.c +++ b/src/panfrost/vulkan/panvk_image.c @@ -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; }