From 46cb19094411c5904d56e30a6f83cbfc0a0ab52c Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Sat, 21 Jun 2025 15:00:37 +0200 Subject: [PATCH] etnaviv: improve dither enable conditions Take into account the alpha blend state of all render targets when deciding whether it is okay to enable dithering. Do not dither render targets with less than 5 bits of precision per channel as this leads to visible artifacts. GC7000 fails to blend RGBA4444 correctly when dithering is disabled but PE_LOGIC_OP_DITHER_MODE is set to anything other than 0, so set this state depending on whether dithering is actually enabled. Signed-off-by: Lucas Stach Reviewed-by: Christian Gmeiner Part-of: --- src/gallium/drivers/etnaviv/etnaviv_blend.c | 40 +++++++++++++-------- src/gallium/drivers/etnaviv/etnaviv_emit.c | 4 +-- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/gallium/drivers/etnaviv/etnaviv_blend.c b/src/gallium/drivers/etnaviv/etnaviv_blend.c index 02ed2e618fc..95068634035 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_blend.c +++ b/src/gallium/drivers/etnaviv/etnaviv_blend.c @@ -77,6 +77,13 @@ etna_blend_state_create(struct pipe_context *pctx, rt->alpha_dst_factor == PIPE_BLENDFACTOR_ZERO && rt->alpha_func == PIPE_BLEND_ADD); + /* Dithering together with alpha blending and without feature + * ETNA_FEATURE_PE_DITHER_FIX leads to visibly altered colors. + */ + if (co->rt[i].alpha_enable && + !VIV_FEATURE(ctx->screen, ETNA_FEATURE_PE_DITHER_FIX)) + co->base.dither = 0; + /* Enable separate alpha if * - Blending enabled (see above) * - NOT source/destination factor and eq is same for both rgb and alpha @@ -92,23 +99,9 @@ etna_blend_state_create(struct pipe_context *pctx, co->PE_LOGIC_OP = VIVS_PE_LOGIC_OP_OP(logicop_enable ? so->logicop_func : LOGIC_OP_COPY) | - VIVS_PE_LOGIC_OP_DITHER_MODE(3) | /* TODO: related to dithering, sometimes 2 */ 0x000E4000 /* ??? */; /* XXX alpha_to_coverage / alpha_to_one? */ - /* Set dither registers based on dither status. These registers set the - * dither pattern, - * for now, set the same values as the blob. - */ - if (so->dither && - (!co->rt[0].alpha_enable || - VIV_FEATURE(ctx->screen, ETNA_FEATURE_PE_DITHER_FIX))) { - co->PE_DITHER[0] = 0x6e4ca280; - co->PE_DITHER[1] = 0x5d7f91b3; - } else { - co->PE_DITHER[0] = 0xffffffff; - co->PE_DITHER[1] = 0xffffffff; - } return co; } @@ -120,6 +113,7 @@ etna_update_blend(struct etna_context *ctx) struct pipe_blend_state *pblend = ctx->blend; struct etna_blend_state *blend = etna_blend_state(pblend); unsigned current_rt = 0; + bool dither_allow = true; for (unsigned i = 0; i < pfb->nr_cbufs; i++) { if (!pfb->cbufs[i].texture) @@ -143,6 +137,13 @@ etna_update_blend(struct etna_context *ctx) colormask = rt->colormask; } + /* Dithering a 4bpc format leads to visible artifacts due to the low + * precision of the color channels. + */ + if (pfb->cbufs[i].format == PIPE_FORMAT_B4G4R4A4_UNORM || + pfb->cbufs[i].format == PIPE_FORMAT_B4G4R4X4_UNORM) + dither_allow = false; + /* If the complete render target is written, set full_overwrite: * - The color mask covers all channels of the render target * - No blending or logicop is used @@ -181,6 +182,17 @@ etna_update_blend(struct etna_context *ctx) if (current_rt == 0) blend->rt[0].PE_COLOR_FORMAT = VIVS_PE_COLOR_FORMAT_OVERWRITE; + /* Use same dither pattern as the blob */ + if (blend->base.dither && dither_allow) { + blend->PE_DITHER[0] = 0x6e4ca280; + blend->PE_DITHER[1] = 0x5d7f91b3; + blend->PE_LOGIC_OP |= VIVS_PE_LOGIC_OP_DITHER_MODE(3); /* TODO: sometimes 2 */ + } else { + blend->PE_DITHER[0] = 0xffffffff; + blend->PE_DITHER[1] = 0xffffffff; + blend->PE_LOGIC_OP &= ~VIVS_PE_LOGIC_OP_DITHER_MODE__MASK; + } + return true; } diff --git a/src/gallium/drivers/etnaviv/etnaviv_emit.c b/src/gallium/drivers/etnaviv/etnaviv_emit.c index 4921002da55..97b214ca59e 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_emit.c +++ b/src/gallium/drivers/etnaviv/etnaviv_emit.c @@ -543,9 +543,7 @@ etna_emit_state(struct etna_context *ctx) if (unlikely(dirty & (ETNA_DIRTY_BLEND | ETNA_DIRTY_FRAMEBUFFER))) { struct etna_blend_state *blend = etna_blend_state(ctx->blend); /*014A4*/ EMIT_STATE(PE_LOGIC_OP, blend->PE_LOGIC_OP | ctx->framebuffer.PE_LOGIC_OP); - } - if (unlikely(dirty & (ETNA_DIRTY_BLEND))) { - struct etna_blend_state *blend = etna_blend_state(ctx->blend); + for (int x = 0; x < 2; ++x) { /*014A8*/ EMIT_STATE(PE_DITHER(x), blend->PE_DITHER[x]); }