etnaviv: blt: Add BLT format conversion support
Some checks are pending
macOS-CI / macOS-CI (dri) (push) Waiting to run
macOS-CI / macOS-CI (xlib) (push) Waiting to run

Until now the BLT engine only handled same-format blits. Teach it to
convert between the UNORM formats it can represent, so format-converting
blits no longer fall back to the 3D blitter.

The supported formats are A8R8G8B8, X8R8G8B8, A4R4G4B4, A1R5G5B5,
R5G6B5, R8G8, R8 and A2R10G10B10.

The BLT format names are BGRA-based, matching the PE-internal byte order,
so an identity swizzle is correct for all of these except A2R10G10B10.
That one the PE keeps in RGBA order, so pipe R lands in the BLT B position
and vice versa. blt_conversion_needs_channel_swap() captures this and
find_blt_conversion() derives the per-image swizzle.

SRGB variants share the BLT format of their UNORM sibling. For example
R8G8B8A8_UNORM and R8G8B8A8_SRGB both map to BLT_FORMAT_A8R8G8B8, and
the sRGB handling is carried separately via img->srgb.

No regressions in dEQP-GLES3.functional.fbo.blit.conversion.*.

Signed-off-by: Christian Gmeiner <cgmeiner@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/39879>
This commit is contained in:
Christian Gmeiner 2026-05-26 08:57:21 +02:00 committed by Marge Bot
parent 4e5363a66c
commit 99400f272d

View file

@ -49,6 +49,82 @@
#include <assert.h>
static const uint32_t blt_conversion_formats[] = {
BLT_FORMAT_A8R8G8B8,
BLT_FORMAT_X8R8G8B8,
BLT_FORMAT_A4R4G4B4,
BLT_FORMAT_A1R5G5B5,
BLT_FORMAT_R5G6B5,
BLT_FORMAT_R8G8,
BLT_FORMAT_R8,
BLT_FORMAT_A2R10G10B10,
};
static inline bool
blt_conversion_needs_channel_swap(uint32_t blt_fmt)
{
/* Not the PE_FORMAT_RB_SWAP property: RB_SWAP formats are stored BGRA and
* already match the BGRA-named BLT formats (identity), and R8/R8G8 have no
* R/B pair. A2R10G10B10 is the only whitelist format stored RGBA against a
* BGRA-named BLT format, so it alone needs the swap.
*/
return blt_fmt == BLT_FORMAT_A2R10G10B10;
}
struct blt_conv_swizzle {
uint8_t src_swizzle[4];
uint8_t dst_swizzle[4];
};
static bool
find_blt_conversion(enum pipe_format src_pipe, enum pipe_format dst_pipe,
struct blt_conv_swizzle *swizzle)
{
const uint32_t src_blt = translate_blt_format(src_pipe);
const uint32_t dst_blt = translate_blt_format(dst_pipe);
if (src_blt == ETNA_NO_MATCH || dst_blt == ETNA_NO_MATCH)
return false;
bool src_found = false, dst_found = false;
for (unsigned i = 0; i < ARRAY_SIZE(blt_conversion_formats); i++) {
if (blt_conversion_formats[i] == src_blt) src_found = true;
if (blt_conversion_formats[i] == dst_blt) dst_found = true;
}
if (!src_found || !dst_found)
return false;
static const uint8_t ident[] = {0, 1, 2, 3};
static const uint8_t rb_swap[] = {2, 1, 0, 3};
memcpy(swizzle->src_swizzle,
blt_conversion_needs_channel_swap(src_blt) ? rb_swap : ident, 4);
memcpy(swizzle->dst_swizzle,
blt_conversion_needs_channel_swap(dst_blt) ? rb_swap : ident, 4);
return true;
}
/* do_blit_framebuffer(..) only sets non-identity swizzle for channels
* missing from src - shared channels stay identity.
*/
static inline void
blt_assert_swizzle_compatible(ASSERTED const struct pipe_blit_info *info)
{
if (!info->swizzle_enable)
return;
/* Only channels present in both src and dst must be identity. */
ASSERTED unsigned n =
MIN2(util_format_get_nr_components(info->src.format),
util_format_get_nr_components(info->dst.format));
assert(info->swizzle[0] == PIPE_SWIZZLE_X);
assert(n < 2 || info->swizzle[1] == PIPE_SWIZZLE_Y);
assert(n < 3 || info->swizzle[2] == PIPE_SWIZZLE_Z);
}
static uint32_t
etna_compatible_blt_format(enum pipe_format fmt)
{
@ -678,35 +754,46 @@ etna_try_blt_blit(struct pipe_context *pctx,
return false;
}
/* Only support same format (used tiling/detiling) blits for now.
* TODO: figure out which different-format blits are possible and test them
* - need to use correct swizzle
* - set sRGB bits correctly
* - avoid trying to convert between float/int formats?
*/
if (blit_info->src.format != blit_info->dst.format) {
DBG("non matching formats: %s vs %s",
util_format_short_name(blit_info->src.format),
util_format_short_name(blit_info->dst.format));
return false;
struct blt_conv_swizzle conv_swizzle;
bool has_conversion = false;
enum pipe_format src_fmt = blit_info->src.format;
enum pipe_format dst_fmt = blit_info->dst.format;
if (src_fmt != dst_fmt) {
has_conversion = find_blt_conversion(src_fmt, dst_fmt, &conv_swizzle);
if (!has_conversion) {
DBG("format conversion not supported: %s -> %s",
util_format_short_name(src_fmt),
util_format_short_name(dst_fmt));
return false;
}
blt_assert_swizzle_compatible(blit_info);
}
const enum pipe_format fmt = translate_format_128bit_to_64bit(blit_info->dst.format);
src_fmt = translate_format_128bit_to_64bit(src_fmt);
dst_fmt = translate_format_128bit_to_64bit(dst_fmt);
/* try to find a exact format match first */
uint32_t format = translate_blt_format(fmt);
/* try to find an exact format match first */
uint32_t src_format = translate_blt_format(src_fmt);
uint32_t dst_format = translate_blt_format(dst_fmt);
/* When not resolving MSAA, but only doing a layout conversion, we can get
* away with a fallback format of matching size.
*/
if (format == ETNA_NO_MATCH && !downsample_x && !downsample_y)
format = etna_compatible_blt_format(fmt);
if (format == ETNA_NO_MATCH) {
DBG("format not supported: %s", util_format_short_name(fmt));
if (src_format == ETNA_NO_MATCH && !downsample_x && !downsample_y)
src_format = etna_compatible_blt_format(src_fmt);
if (dst_format == ETNA_NO_MATCH && !downsample_x && !downsample_y)
dst_format = etna_compatible_blt_format(dst_fmt);
if (src_format == ETNA_NO_MATCH || dst_format == ETNA_NO_MATCH) {
DBG("format not supported: %s -> %s",
util_format_short_name(src_fmt), util_format_short_name(dst_fmt));
return false;
}
if (blit_info->scissor_enable ||
blit_info->swizzle_enable ||
(blit_info->swizzle_enable && !has_conversion) ||
blit_info->dst.box.depth != blit_info->src.box.depth ||
blit_info->dst.box.depth != 1) {
return false;
@ -758,18 +845,22 @@ etna_try_blt_blit(struct pipe_context *pctx,
} else {
/* Copy op */
struct blt_imgcopy_op op = {};
static const uint8_t ident[] = {0, 1, 2, 3};
op.src.addr.bo = src->bo;
op.src.addr.offset = src_lev->offset + blit_info->src.box.z * src_lev->layer_stride;
op.src.addr.flags = ETNA_RELOC_READ;
op.src.bpp = util_format_get_blocksize(blit_info->src.format);
op.src.format = format;
op.src.format = src_format;
op.src.srgb = util_format_is_srgb(src_fmt);
op.src.stride = src_lev->stride;
op.src.tiling = src->layout;
op.src.downsample_x = downsample_x;
op.src.downsample_y = downsample_y;
for (unsigned x=0; x<4; ++x)
op.src.swizzle[x] = x;
if (has_conversion)
memcpy(op.src.swizzle, conv_swizzle.src_swizzle, 4);
else
memcpy(op.src.swizzle, ident, 4);
if (etna_resource_level_ts_valid(src_lev)) {
op.src.use_ts = 1;
@ -785,11 +876,14 @@ etna_try_blt_blit(struct pipe_context *pctx,
op.dest.addr.bo = dst->bo;
op.dest.addr.offset = dst_lev->offset + blit_info->dst.box.z * dst_lev->layer_stride;
op.dest.addr.flags = ETNA_RELOC_WRITE;
op.dest.format = format;
op.dest.format = dst_format;
op.dest.srgb = util_format_is_srgb(dst_fmt);
op.dest.stride = dst_lev->stride;
op.dest.tiling = dst->layout;
for (unsigned x=0; x<4; ++x)
op.dest.swizzle[x] = x;
if (has_conversion)
memcpy(op.dest.swizzle, conv_swizzle.dst_swizzle, 4);
else
memcpy(op.dest.swizzle, ident, 4);
/* Apply R<->B swizzle when needed:
* - Transfer blits (CPU access): swap on the linear side to convert