From cfa8deaad97f72c270cf32da53d24b1c66d6ddf3 Mon Sep 17 00:00:00 2001 From: Christian Gmeiner Date: Sun, 22 Feb 2026 01:03:01 +0100 Subject: [PATCH] etnaviv: Select texture format dynamically for shared RB_SWAP resources The previous patches statically remapped the texture format at sampler view creation time (A8R8G8B8 -> A8B8G8R8) for shared resources with RB_SWAP formats. This is correct when the shared buffer holds externally-produced RGBA data (import) or after flush_resource has done the R<->B swap. However, if the PE renders directly to a shared buffer (LINEAR_PE, no render shadow), the data becomes BGRA. The static A8B8G8R8 format then reads BGRA as RGBA - R and B are swapped. Fix this by tracking whether the shared buffer holds native (RGBA) or PE-internal (BGRA) byte order via a new shared_native_order flag, and selecting the texture format dynamically at emit time. The flag is set to true on import and export (data is native byte order at that point), cleared when the PE renders directly to a shared buffer without a render shadow, and set back to true after flush_resource performs the R<->B swap. Both the state-based and descriptor-based texture paths pre-compute the native format variant at sampler view creation time, avoiding any per-frame overhead beyond a single branch in the emit path. Fixes: a5766e75e48 ("etnaviv: Use BGRA-internal texture format with BLT/RS R/B swizzle") Signed-off-by: Christian Gmeiner Part-of: --- src/gallium/drivers/etnaviv/etnaviv_context.c | 6 ++ src/gallium/drivers/etnaviv/etnaviv_format.c | 8 +-- .../drivers/etnaviv/etnaviv_resource.c | 6 ++ .../drivers/etnaviv/etnaviv_resource.h | 3 + .../drivers/etnaviv/etnaviv_texture_desc.c | 57 +++++++++++++++---- .../drivers/etnaviv/etnaviv_texture_state.c | 37 +++++++++--- 6 files changed, 95 insertions(+), 22 deletions(-) diff --git a/src/gallium/drivers/etnaviv/etnaviv_context.c b/src/gallium/drivers/etnaviv/etnaviv_context.c index c0e56ebecb1..afceda7861d 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_context.c +++ b/src/gallium/drivers/etnaviv/etnaviv_context.c @@ -556,10 +556,16 @@ etna_draw_vbo(struct pipe_context *pctx, const struct pipe_draw_info *info, for (i = 0; i < pfb->nr_cbufs; i++) { if (pfb->cbufs[i].texture) { + struct etna_resource *rsc = etna_resource(pfb->cbufs[i].texture); struct etna_resource *res = etna_resource_get_render_compatible(pctx, pfb->cbufs[i].texture); struct etna_resource_level *level = &res->levels[pfb->cbufs[i].level]; etna_resource_level_mark_changed(level); + + /* PE rendered directly to the shared buffer (no render shadow). + * Data is now in PE-internal byte order (BGRA for RB_SWAP formats). */ + if (rsc->shared && res == rsc) + rsc->shared_native_order = false; } } diff --git a/src/gallium/drivers/etnaviv/etnaviv_format.c b/src/gallium/drivers/etnaviv/etnaviv_format.c index 547eee49b4d..2e423fefce3 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_format.c +++ b/src/gallium/drivers/etnaviv/etnaviv_format.c @@ -395,10 +395,10 @@ translate_pe_format_rb_swap(enum pipe_format fmt) return formats[fmt].pe & PE_FORMAT_RB_SWAP; } -/* For shared resources with RB_SWAP formats, remaps the HW texture format - * back to the one matching the actual byte order in memory (e.g., A8B8G8R8 - * for RGBA data). Normally we use A8R8G8B8 to match PE-internal BGRA byte - * order, but shared resources store data in the standard byte order. +/* For RB_SWAP formats, remaps the HW texture format to the one matching + * native byte order in memory (e.g., A8B8G8R8 for RGBA data). Normally we + * use A8R8G8B8 to match PE-internal BGRA byte order, but shared resources + * that have been flushed store data in the standard byte order. */ uint32_t remap_texture_format_rb_swap(uint32_t format) diff --git a/src/gallium/drivers/etnaviv/etnaviv_resource.c b/src/gallium/drivers/etnaviv/etnaviv_resource.c index 703d8e449ec..e4fe4640d45 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_resource.c +++ b/src/gallium/drivers/etnaviv/etnaviv_resource.c @@ -835,6 +835,7 @@ etna_resource_from_handle(struct pipe_screen *pscreen, rsc->modifier = modifier; rsc->shared = true; + rsc->shared_native_order = true; if (usage & PIPE_HANDLE_USAGE_EXPLICIT_FLUSH) rsc->explicit_flush = true; @@ -937,7 +938,12 @@ etna_resource_get_handle(struct pipe_screen *pscreen, } handle->modifier = etna_resource_modifier(rsc); + /* Mark as shared. Data is in native byte order at export time (either + * empty or CPU-written). draw_vbo will set shared_native_order = false + * when the PE renders to this resource. */ rsc->shared = true; + rsc->shared_native_order = true; + if (!(usage & PIPE_HANDLE_USAGE_EXPLICIT_FLUSH)) rsc->explicit_flush = false; diff --git a/src/gallium/drivers/etnaviv/etnaviv_resource.h b/src/gallium/drivers/etnaviv/etnaviv_resource.h index fd6f9a24b45..a4a531cdca3 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_resource.h +++ b/src/gallium/drivers/etnaviv/etnaviv_resource.h @@ -227,6 +227,9 @@ struct etna_resource { bool explicit_flush; /* resource is shared outside of the screen */ bool shared; + /* shared buffer has standard byte order (RGBA for R8G8B8A8_UNORM). + * false when PE has written BGRA directly to the shared buffer. */ + bool shared_native_order; struct pipe_box *damage; unsigned num_damage; diff --git a/src/gallium/drivers/etnaviv/etnaviv_texture_desc.c b/src/gallium/drivers/etnaviv/etnaviv_texture_desc.c index 013872d6c0f..75164c76cbd 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_texture_desc.c +++ b/src/gallium/drivers/etnaviv/etnaviv_texture_desc.c @@ -64,9 +64,12 @@ struct etna_sampler_view_desc { uint32_t SAMP_CTRL0; uint32_t SAMP_CTRL0_MASK; uint32_t SAMP_CTRL1; + bool has_rb_swap; struct pipe_resource *res; - struct etna_reloc DESC_ADDR[2]; /* [0] = seamless disabled, [1] = seamless enabled */ + /* [0]=non-seamless PE-internal, [1]=seamless PE-internal, + * [2]=non-seamless native, [3]=seamless native */ + struct etna_reloc DESC_ADDR[4]; struct etna_sampler_ts ts; }; @@ -139,11 +142,10 @@ etna_create_sampler_view_desc(struct pipe_context *pctx, struct pipe_resource *p struct etna_context *ctx = etna_context(pctx); uint32_t format = translate_texture_format(so->format); - /* Shared resources store data in standard byte order (e.g., RGBA for - * R8G8B8A8_UNORM). Use the native texture format to read them correctly, - * bypassing the BGRA-internal optimization used for internal resources. */ - if (etna_resource(prsc)->shared && translate_pe_format_rb_swap(so->format)) - format = remap_texture_format_rb_swap(format); + /* For RB_SWAP formats, pre-compute the alternative texture format for when + * shared resources hold data in native byte order (RGBA). */ + bool rb_swap = translate_pe_format_rb_swap(so->format); + uint32_t native_format = rb_swap ? remap_texture_format_rb_swap(format) : 0; const bool ext = !!(format & EXT_FORMAT); const bool astc = !!(format & ASTC_FORMAT); @@ -177,8 +179,10 @@ etna_create_sampler_view_desc(struct pipe_context *pctx, struct pipe_resource *p if (util_format_is_srgb(so->format)) sv->SAMP_CTRL1 |= VIVS_NTE_DESCRIPTOR_SAMP_CTRL1_SRGB; - /* Create two texture descriptors: one with seamless cube map disabled, one enabled */ - u_suballocator_alloc(&ctx->tex_desc_allocator, TEXTURE_DESC_SIZE * 2, 64, + /* Create texture descriptors: 2 normally (non-seamless + seamless), + * or 4 for RB_SWAP formats (2 PE-internal + 2 native byte order). */ + unsigned num_descs = rb_swap ? 4 : 2; + u_suballocator_alloc(&ctx->tex_desc_allocator, TEXTURE_DESC_SIZE * num_descs, 64, &suballoc_offset, &sv->res); if (!sv->res) goto error; @@ -250,7 +254,7 @@ etna_create_sampler_view_desc(struct pipe_context *pctx, struct pipe_resource *p memcpy(seamless, buf, TEXTURE_DESC_SIZE); seamless[(TEXDESC_CONFIG1) >> 2] |= VIVS_TE_SAMPLER_CONFIG1_SEAMLESS_CUBE_MAP; - /* Setup relocations for both descriptors. */ + /* Setup relocations for PE-internal format descriptors. */ sv->DESC_ADDR[0].bo = etna_buffer_resource(sv->res)->bo; sv->DESC_ADDR[0].offset = suballoc_offset; sv->DESC_ADDR[0].flags = ETNA_RELOC_READ; @@ -259,6 +263,30 @@ etna_create_sampler_view_desc(struct pipe_context *pctx, struct pipe_resource *p sv->DESC_ADDR[1].offset = suballoc_offset + TEXTURE_DESC_SIZE; sv->DESC_ADDR[1].flags = ETNA_RELOC_READ; + if (rb_swap) { + /* Build descriptors 2,3 with native byte order format. + * Copy from descriptors 0,1 and patch the CONFIG0 format field. */ + uint32_t *native = buf + 2 * (TEXTURE_DESC_SIZE / sizeof(uint32_t)); + memcpy(native, buf, TEXTURE_DESC_SIZE); + native[(TEXDESC_CONFIG0) >> 2] = (native[(TEXDESC_CONFIG0) >> 2] & + ~VIVS_TE_SAMPLER_CONFIG0_FORMAT__MASK) | + VIVS_TE_SAMPLER_CONFIG0_FORMAT(native_format); + + uint32_t *native_seamless = native + (TEXTURE_DESC_SIZE / sizeof(uint32_t)); + memcpy(native_seamless, native, TEXTURE_DESC_SIZE); + native_seamless[(TEXDESC_CONFIG1) >> 2] |= VIVS_TE_SAMPLER_CONFIG1_SEAMLESS_CUBE_MAP; + + sv->DESC_ADDR[2].bo = etna_buffer_resource(sv->res)->bo; + sv->DESC_ADDR[2].offset = suballoc_offset + 2 * TEXTURE_DESC_SIZE; + sv->DESC_ADDR[2].flags = ETNA_RELOC_READ; + + sv->DESC_ADDR[3].bo = etna_buffer_resource(sv->res)->bo; + sv->DESC_ADDR[3].offset = suballoc_offset + 3 * TEXTURE_DESC_SIZE; + sv->DESC_ADDR[3].flags = ETNA_RELOC_READ; + + sv->has_rb_swap = true; + } + return &sv->base; error: @@ -361,8 +389,17 @@ etna_emit_texture_desc(struct etna_context *ctx) if ((1 << x) & active_samplers) { struct etna_sampler_state_desc *ss = etna_sampler_state_desc(ctx->sampler[x]); struct etna_sampler_view_desc *sv = etna_sampler_view_desc(ctx->sampler_view[x]); + int desc_base = 0; + + if (sv->has_rb_swap) { + struct etna_resource *rsc = etna_resource(sv->base.texture); + + if (rsc->shared && rsc->shared_native_order) + desc_base = 2; + } + struct etna_reloc *desc_addr = ss->base.seamless_cube_map ? - &sv->DESC_ADDR[1] : &sv->DESC_ADDR[0]; + &sv->DESC_ADDR[desc_base + 1] : &sv->DESC_ADDR[desc_base]; etna_sampler_view_update_descriptor(ctx, stream, sv); etna_set_state_reloc(stream, VIVS_NTE_DESCRIPTOR_ADDR(x), desc_addr); } else if ((1 << x) & ctx->prev_active_samplers){ diff --git a/src/gallium/drivers/etnaviv/etnaviv_texture_state.c b/src/gallium/drivers/etnaviv/etnaviv_texture_state.c index ddde0dd8152..675d0c26abf 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_texture_state.c +++ b/src/gallium/drivers/etnaviv/etnaviv_texture_state.c @@ -63,6 +63,8 @@ struct etna_sampler_view { /* sampler offset +4*sampler, interleave when committing state */ uint32_t config0; uint32_t config0_mask; + uint32_t config0_native; /* config0 with native byte order format (A8B8G8R8) */ + bool has_rb_swap; uint32_t config1; uint32_t config_3d; uint32_t size; @@ -81,6 +83,21 @@ etna_sampler_view(struct pipe_sampler_view *view) return (struct etna_sampler_view *)view; } +/* Return the config0 value for the sampler view, selecting between PE-internal + * format and native byte order format based on the resource's shared state. */ +static inline uint32_t +etna_sampler_view_config0(struct etna_sampler_view *sv) +{ + if (sv->has_rb_swap) { + struct etna_resource *rsc = etna_resource(sv->base.texture); + + if (rsc->shared && rsc->shared_native_order) + return sv->config0_native; + } + + return sv->config0; +} + static void * etna_create_sampler_state_state(struct pipe_context *pipe, const struct pipe_sampler_state *ss) @@ -166,11 +183,10 @@ etna_create_sampler_view_state(struct pipe_context *pctx, struct pipe_resource * struct etna_screen *screen = ctx->screen; uint32_t format = translate_texture_format(so->format); - /* Shared resources store data in standard byte order (e.g., RGBA for - * R8G8B8A8_UNORM). Use the native texture format to read them correctly, - * bypassing the BGRA-internal optimization used for internal resources. */ - if (etna_resource(prsc)->shared && translate_pe_format_rb_swap(so->format)) - format = remap_texture_format_rb_swap(format); + /* For RB_SWAP formats, pre-compute the alternative texture format for when + * shared resources hold data in native byte order (RGBA). */ + const bool rb_swap = translate_pe_format_rb_swap(so->format); + const uint32_t native_format = rb_swap ? remap_texture_format_rb_swap(format) : 0; const bool ext = !!(format & EXT_FORMAT); const bool astc = !!(format & ASTC_FORMAT); @@ -278,6 +294,12 @@ etna_create_sampler_view_state(struct pipe_context *pctx, struct pipe_resource * VIVS_TE_SAMPLER_CONFIG0_VWRAP(TEXTURE_WRAPMODE_CLAMP_TO_EDGE); } + if (rb_swap) { + sv->config0_native = (sv->config0 & ~VIVS_TE_SAMPLER_CONFIG0_FORMAT__MASK) | + VIVS_TE_SAMPLER_CONFIG0_FORMAT(native_format); + sv->has_rb_swap = true; + } + return &sv->base; } @@ -362,7 +384,7 @@ etna_emit_new_texture_state(struct etna_context *ctx) struct etna_sampler_state *ss = etna_sampler_state(ctx->sampler[x]); struct etna_sampler_view *sv = etna_sampler_view(ctx->sampler_view[x]); - val = (ss->config0 & sv->config0_mask) | sv->config0; + val = (ss->config0 & sv->config0_mask) | etna_sampler_view_config0(sv); if (util_format_description(sv->base.format)->colorspace == UTIL_FORMAT_COLORSPACE_ZS && ss->base.min_mip_filter == PIPE_TEX_MIPFILTER_LINEAR) { @@ -507,8 +529,7 @@ etna_emit_texture_state(struct etna_context *ctx) if ((1 << x) & active_samplers) { struct etna_sampler_state *ss = etna_sampler_state(ctx->sampler[x]); struct etna_sampler_view *sv = etna_sampler_view(ctx->sampler_view[x]); - - val = (ss->config0 & sv->config0_mask) | sv->config0; + val = (ss->config0 & sv->config0_mask) | etna_sampler_view_config0(sv); if (util_format_description(sv->base.format)->colorspace == UTIL_FORMAT_COLORSPACE_ZS && ss->base.min_mip_filter == PIPE_TEX_MIPFILTER_LINEAR) {