etnaviv: Convert PE-internal BGRA to RGBA when flushing shared resources

The PE renders RB_SWAP formats (e.g. R8G8B8A8_UNORM) using the A8R8G8B8
hardware format, which stores data in BGRA byte order internally. For
shared/dmabuf resources, external consumers expect the byte order
mandated by the DRM FourCC.

Hook into flush_resource to apply an R<->B swap during the existing
shadow-to-base copy, using the BLT dest swizzle or RS SWAP_RB.

Fixes: a5766e75e48 ("etnaviv: Use BGRA-internal texture format with BLT/RS R/B swizzle")
Signed-off-by: Christian Gmeiner <cgmeiner@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40029>
This commit is contained in:
Christian Gmeiner 2026-02-21 01:01:50 +01:00 committed by Marge Bot
parent 377f2ec49c
commit 4172e9a918
8 changed files with 92 additions and 25 deletions

View file

@ -728,7 +728,8 @@ etna_try_blt_blit(struct pipe_context *pctx,
/* Flush destination, as the blit will invalidate any pending TS changes. */
if (dst != src && etna_resource_level_needs_flush(dst_lev))
etna_copy_resource(pctx, &dst->base, &dst->base,
blit_info->dst.level, blit_info->dst.level);
blit_info->dst.level, blit_info->dst.level,
false);
/* Kick off BLT here */
if (src == dst && src_lev->ts_compress_fmt < 0) {
@ -788,11 +789,17 @@ etna_try_blt_blit(struct pipe_context *pctx,
for (unsigned x=0; x<4; ++x)
op.dest.swizzle[x] = x;
/* For transfer blits of RB_SWAP formats, apply R<->B swizzle on the
* linear side to convert between GPU-internal BGRA and CPU RGBA. */
if (ctx->in_transfer_blit &&
translate_pe_format_rb_swap(blit_info->src.format) &&
!src->shared && !dst->shared) {
/* Apply R<->B swizzle when needed:
* - Transfer blits (CPU access): swap on the linear side to convert
* between GPU-internal BGRA and CPU RGBA byte order.
* - Shared resource flushes: swap dest to convert PE-internal BGRA
* back to the standard RGBA byte order for external consumers. */
if (ctx->blit_rb_swap) {
op.dest.swizzle[0] = 2; /* R from B position */
op.dest.swizzle[2] = 0; /* B from R position */
} else if (ctx->in_transfer_blit &&
translate_pe_format_rb_swap(blit_info->src.format) &&
!src->shared && !dst->shared) {
bool src_linear = src->layout == ETNA_LAYOUT_LINEAR;
bool dst_linear = dst->layout == ETNA_LAYOUT_LINEAR;

View file

@ -251,26 +251,66 @@ etna_resource_copy_region(struct pipe_context *pctx, struct pipe_resource *dst,
static void
etna_flush_resource(struct pipe_context *pctx, struct pipe_resource *prsc)
{
struct etna_context *ctx = etna_context(pctx);
struct etna_resource *rsc = etna_resource(prsc);
/* When flushing a shared resource with an RB_SWAP format, the PE has
* written BGRA bytes internally. Convert to RGBA during the flush copy
* so the shared buffer has the correct byte order for external consumers. */
const bool flush_rb_swap = rsc->shared &&
translate_pe_format_rb_swap(prsc->format);
if (rsc->render) {
if (etna_resource_older(rsc, etna_resource(rsc->render))) {
if (rsc->damage) {
for (unsigned i = 0; i < rsc->num_damage; i++) {
etna_copy_resource_box(pctx, prsc, rsc->render, 0, 0, &rsc->damage[i]);
etna_copy_resource_box(pctx, prsc, rsc->render, 0, 0,
&rsc->damage[i], flush_rb_swap);
}
} else {
etna_copy_resource(pctx, prsc, rsc->render, 0, 0);
etna_copy_resource(pctx, prsc, rsc->render, 0, 0, flush_rb_swap);
}
if (flush_rb_swap)
rsc->shared_native_order = true;
}
} else if (!etna_resource_ext_ts(rsc) && etna_resource_needs_flush(rsc)) {
etna_copy_resource(pctx, prsc, prsc, 0, 0);
etna_copy_resource(pctx, prsc, prsc, 0, 0, flush_rb_swap);
if (flush_rb_swap) {
rsc->shared_native_order = true;
etna_resource_level_mark_changed(&rsc->levels[0]);
}
} else if (flush_rb_swap) {
/* No render shadow and no TS — PE rendered directly into the shared
* buffer. We still need to R<->B swap the contents.
* Can't use etna_copy_resource(src==dst) here because it skips levels
* without valid TS, so issue the blit directly. */
assert(prsc->last_level == 0);
struct etna_resource_level *lev = &rsc->levels[0];
struct pipe_blit_info blit = {
.mask = util_format_get_mask(prsc->format),
.filter = PIPE_TEX_FILTER_NEAREST,
.src.resource = blit.dst.resource = prsc,
.src.format = blit.dst.format = prsc->format,
.src.box.width = blit.dst.box.width = lev->width,
.src.box.height = blit.dst.box.height = lev->height,
.src.box.depth = blit.dst.box.depth = 1,
};
ctx->blit_rb_swap = true;
ctx->blit(pctx, &blit);
ctx->blit_rb_swap = false;
rsc->shared_native_order = true;
etna_resource_level_mark_changed(&rsc->levels[0]);
}
}
void
etna_copy_resource(struct pipe_context *pctx, struct pipe_resource *dst,
struct pipe_resource *src, int first_level, int last_level)
struct pipe_resource *src, int first_level, int last_level,
bool rb_swap)
{
struct etna_context *ctx = etna_context(pctx);
struct etna_resource *src_priv = etna_resource(src);
@ -280,6 +320,8 @@ etna_copy_resource(struct pipe_context *pctx, struct pipe_resource *dst,
assert(src->array_size == dst->array_size);
assert(last_level <= dst->last_level && last_level <= src->last_level);
ctx->blit_rb_swap = rb_swap;
struct pipe_blit_info blit = {};
blit.mask = util_format_get_mask(dst->format);
blit.filter = PIPE_TEX_FILTER_NEAREST;
@ -324,12 +366,14 @@ etna_copy_resource(struct pipe_context *pctx, struct pipe_resource *dst,
else
etna_resource_level_copy_seqno(&dst_priv->levels[level], &src_priv->levels[level]);
}
ctx->blit_rb_swap = false;
}
void
etna_copy_resource_box(struct pipe_context *pctx, struct pipe_resource *dst,
struct pipe_resource *src, int dst_level, int src_level,
struct pipe_box *box)
struct pipe_box *box, bool rb_swap)
{
struct etna_context *ctx = etna_context(pctx);
struct etna_resource *src_priv = etna_resource(src);
@ -339,6 +383,8 @@ etna_copy_resource_box(struct pipe_context *pctx, struct pipe_resource *dst,
assert(src->array_size == dst->array_size);
assert(!etna_resource_level_needs_flush(&dst_priv->levels[dst_level]));
ctx->blit_rb_swap = rb_swap;
struct pipe_blit_info blit = {};
blit.mask = util_format_get_mask(dst->format);
blit.filter = PIPE_TEX_FILTER_NEAREST;
@ -366,6 +412,8 @@ etna_copy_resource_box(struct pipe_context *pctx, struct pipe_resource *dst,
else
etna_resource_level_copy_seqno(&dst_priv->levels[dst_level],
&src_priv->levels[src_level]);
ctx->blit_rb_swap = false;
}
void

View file

@ -35,12 +35,13 @@ struct etna_context;
void
etna_copy_resource(struct pipe_context *pctx, struct pipe_resource *dst,
struct pipe_resource *src, int first_level, int last_level);
struct pipe_resource *src, int first_level, int last_level,
bool rb_swap);
void
etna_copy_resource_box(struct pipe_context *pctx, struct pipe_resource *dst,
struct pipe_resource *src, int dst_level, int src_level,
struct pipe_box *box);
struct pipe_box *box, bool rb_swap);
void
etna_blit_save_state(struct etna_context *ctx, bool render_cond);

View file

@ -243,6 +243,11 @@ struct etna_context {
bool compute_only;
bool in_draw_vbo;
bool in_transfer_blit;
/* Set by etna_copy_resource/etna_copy_resource_box when the caller
* needs an R<->B swap during the blit. Consumed by BLT/RS because
* pipe_blit_info has no driver-private field to carry this through. */
bool blit_rb_swap;
bool needs_gpu_state_reset;
bool alpha_coverage_dither_emitted;

View file

@ -460,7 +460,7 @@ etna_blit_clear_zs_rs(struct pipe_context *pctx, struct pipe_surface *dst,
/* If the level has valid TS state we need to flush it, as the regular
* clear will not update the state and we must therefore invalidate it. */
etna_copy_resource(pctx, &dst_res->base, &dst_res->base,
dst->level, dst->level);
dst->level, dst->level, false);
etna_rs_gen_clear_cmd(ctx, dst, dst_res, new_clear_value, new_clear_bits, &rs_state);
@ -813,7 +813,8 @@ etna_try_rs_blit(struct pipe_context *pctx,
/* Flush destination, as the blit will invalidate any pending TS changes. */
if (dst != src && etna_resource_level_needs_flush(dst_lev))
etna_copy_resource(pctx, &dst->base, &dst->base,
blit_info->dst.level, blit_info->dst.level);
blit_info->dst.level, blit_info->dst.level,
false);
/* Always flush color and depth cache together before resolving. This makes
* sure that all previous cache content written by the PE is flushed out
@ -883,9 +884,12 @@ etna_try_rs_blit(struct pipe_context *pctx,
.dest_padded_height = dst_lev->padded_height,
.downsample_x = downsample_x,
.downsample_y = downsample_y,
.swap_rb = ctx->in_transfer_blit &&
translate_pe_format_rb_swap(blit_info->src.format) &&
!src->shared && !dst->shared,
/* Swap R<->B when requested by the caller (shared resource flush) or
* for transfer blits of RB_SWAP formats on non-shared resources. */
.swap_rb = ctx->blit_rb_swap ||
(ctx->in_transfer_blit &&
translate_pe_format_rb_swap(blit_info->src.format) &&
!src->shared && !dst->shared),
.dither = {0xffffffff, 0xffffffff}, // XXX dither when going from 24 to 16 bit?
.clear_mode = VIVS_RS_CLEAR_CONTROL_MODE_DISABLED,
.width = width,

View file

@ -128,7 +128,7 @@ etna_update_render_surface(struct pipe_context *pctx,
if ((to != from) &&
etna_resource_level_older(&to->levels[level], &from->levels[level]))
etna_copy_resource(pctx, &to->base, &from->base, level, level);
etna_copy_resource(pctx, &to->base, &from->base, level, level, false);
}
static void
@ -166,7 +166,7 @@ etna_set_framebuffer_state(struct pipe_context *pctx,
/* Resolve TS if needed */
if (!use_ts) {
etna_copy_resource(pctx, &res->base, &res->base, surf->level, surf->level);
etna_copy_resource(pctx, &res->base, &res->base, surf->level, surf->level, false);
etna_resource_level_ts_mark_invalid(level);
}

View file

@ -182,7 +182,8 @@ etna_update_sampler_source(struct pipe_sampler_view *view, int num)
if ((to != from) && etna_resource_older(to, from)) {
etna_copy_resource(view->context, &to->base, &from->base,
view->u.tex.first_level,
MIN2(view->texture->last_level, view->u.tex.last_level));
MIN2(view->texture->last_level, view->u.tex.last_level),
false);
ctx->dirty |= ETNA_DIRTY_TEXTURE_CACHES;
} else if (to == from) {
if (etna_can_use_sampler_ts(view, num)) {
@ -191,7 +192,8 @@ etna_update_sampler_source(struct pipe_sampler_view *view, int num)
/* Resolve TS if needed */
etna_copy_resource(view->context, &to->base, &from->base,
view->u.tex.first_level,
MIN2(view->texture->last_level, view->u.tex.last_level));
MIN2(view->texture->last_level, view->u.tex.last_level),
false);
ctx->dirty |= ETNA_DIRTY_TEXTURE_CACHES;
}
}

View file

@ -218,7 +218,7 @@ etna_texture_unmap(struct pipe_context *pctx, struct pipe_transfer *ptrans)
if (ptrans->usage & ETNA_PIPE_MAP_DISCARD_LEVEL)
etna_resource_level_mark_flushed(res_level);
else
etna_copy_resource(pctx, &rsc->base, &rsc->base, ptrans->level, ptrans->level);
etna_copy_resource(pctx, &rsc->base, &rsc->base, ptrans->level, ptrans->level, false);
}
if (trans->rsc) {
@ -227,7 +227,7 @@ etna_texture_unmap(struct pipe_context *pctx, struct pipe_transfer *ptrans)
*/
ctx->in_transfer_blit = true;
etna_copy_resource_box(pctx, ptrans->resource, trans->rsc,
ptrans->level, 0, &ptrans->box);
ptrans->level, 0, &ptrans->box, false);
ctx->in_transfer_blit = false;
} else if (trans->staging) {
/* map buffer object */
@ -393,7 +393,7 @@ etna_texture_map(struct pipe_context *pctx, struct pipe_resource *prsc,
if ((usage & PIPE_MAP_READ) || !(usage & ETNA_PIPE_MAP_DISCARD_LEVEL)) {
ctx->in_transfer_blit = true;
etna_copy_resource_box(pctx, trans->rsc, &rsc->base, 0, level, &ptrans->box);
etna_copy_resource_box(pctx, trans->rsc, &rsc->base, 0, level, &ptrans->box, false);
ctx->in_transfer_blit = false;
}