From b965adc60f2db863980a104953437e40684449f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Tue, 10 Feb 2026 13:04:57 +0100 Subject: [PATCH 01/18] pan/crc: Restrict CRC buffer creation to 1st RT mipmap level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restrict the creation of a CRC buffer for an image to the 1st mipmap level. At emit_fbd() time, Transation Elimination is only enabled if CRC is enabled for the selected RT and if its first configured level is 0. This was previously enforced at the Gallium driver level but it needs to be done at the lib level to later support PanVK too. Signed-off-by: Loïc Molinari --- src/gallium/drivers/panfrost/pan_resource.c | 2 +- src/panfrost/lib/pan_image.h | 3 ++- src/panfrost/lib/pan_layout.c | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/gallium/drivers/panfrost/pan_resource.c b/src/gallium/drivers/panfrost/pan_resource.c index d924c3c6cdd..bc1ca6be1ac 100644 --- a/src/gallium/drivers/panfrost/pan_resource.c +++ b/src/gallium/drivers/panfrost/pan_resource.c @@ -830,7 +830,7 @@ panfrost_should_checksum(const struct panfrost_device *dev, return pres->base.bind & PIPE_BIND_RENDER_TARGET && panfrost_is_2d(pres) && bytes_per_pixel <= bytes_per_pixel_max && - pres->base.last_level == 0 && !(dev->debug & PAN_DBG_NO_CRC); + !(dev->debug & PAN_DBG_NO_CRC); } static bool diff --git a/src/panfrost/lib/pan_image.h b/src/panfrost/lib/pan_image.h index 1f9a0db2ec1..e2a1a2a3500 100644 --- a/src/panfrost/lib/pan_image.h +++ b/src/panfrost/lib/pan_image.h @@ -146,7 +146,8 @@ pan_image_view_has_crc(const struct pan_image_view *iview) if (!p.image) return false; - return p.image->props.crc; + /* Only mip level 0 gets a CRC buffer allocated. */ + return p.image->props.crc && iview->first_level == 0; } static inline struct pan_image_plane_ref diff --git a/src/panfrost/lib/pan_layout.c b/src/panfrost/lib/pan_layout.c index 67a47e65e06..b0114493623 100644 --- a/src/panfrost/lib/pan_layout.c +++ b/src/panfrost/lib/pan_layout.c @@ -125,8 +125,8 @@ pan_image_layout_init( layout_constraints.offset_B += slayout->size_B; - /* Add a checksum region if necessary */ - if (props->crc) { + /* Add a CRC buffer at level 0 if necessary */ + if (l == 0 && props->crc) { init_slice_crc_info(arch, slayout, mip_extent_px.width, mip_extent_px.height, layout_constraints.offset_B); layout_constraints.offset_B += slayout->crc.size_B; From 7fe62246dd8148c0a77a37c77f31afcd7ed971bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Tue, 10 Feb 2026 10:27:34 +0100 Subject: [PATCH 02/18] pan/crc: Introduce pan_fb_is_fully_covered() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add function to retrieve whether the area of a pan_fb_info data structure is fully covered by the draw extent. Signed-off-by: Loïc Molinari --- src/gallium/drivers/panfrost/pan_fb_preload.c | 4 +--- src/panfrost/lib/pan_desc.c | 8 ++------ src/panfrost/lib/pan_desc.h | 9 +++++++++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/gallium/drivers/panfrost/pan_fb_preload.c b/src/gallium/drivers/panfrost/pan_fb_preload.c index fd0ae278d8d..e534a69c8d1 100644 --- a/src/gallium/drivers/panfrost/pan_fb_preload.c +++ b/src/gallium/drivers/panfrost/pan_fb_preload.c @@ -1197,9 +1197,7 @@ pan_preload_emit_pre_frame_dcd(struct pan_fb_preload_cache *cache, * write even clean tiles to make sure CRC data is updated. */ if (crc_rt >= 0) { bool *valid = fb->rts[crc_rt].crc_valid; - bool full = !fb->draw_extent.minx && !fb->draw_extent.miny && - fb->draw_extent.maxx == (fb->width - 1) && - fb->draw_extent.maxy == (fb->height - 1); + bool full = pan_fb_is_fully_covered(fb); if (full && !(*valid)) always_write = true; diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index 3df01de0090..4487f9da57c 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -140,9 +140,7 @@ GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) continue; bool valid = *(fb->rts[i].crc_valid); - bool full = !fb->draw_extent.minx && !fb->draw_extent.miny && - fb->draw_extent.maxx == (fb->width - 1) && - fb->draw_extent.maxy == (fb->height - 1); + bool full = pan_fb_is_fully_covered(fb); if (!full && !valid) continue; @@ -1256,9 +1254,7 @@ GENX(pan_emit_fbd)(const struct pan_fb_info *fb, unsigned layer_idx, if (crc_rt >= 0) { bool *valid = fb->rts[crc_rt].crc_valid; - bool full = !fb->draw_extent.minx && !fb->draw_extent.miny && - fb->draw_extent.maxx == (fb->width - 1) && - fb->draw_extent.maxy == (fb->height - 1); + bool full = pan_fb_is_fully_covered(fb); /* If the CRC was valid it stays valid, if it wasn't, we must ensure * the render operation covers the full frame, and clean tiles are diff --git a/src/panfrost/lib/pan_desc.h b/src/panfrost/lib/pan_desc.h index db5b6588ad3..a85ffbcc013 100644 --- a/src/panfrost/lib/pan_desc.h +++ b/src/panfrost/lib/pan_desc.h @@ -162,6 +162,15 @@ struct pan_clean_tile { uint8_t write_zs : 1; }; +static inline bool +pan_fb_is_fully_covered(const struct pan_fb_info *fb) +{ + return !fb->draw_extent.minx && + !fb->draw_extent.miny && + fb->draw_extent.maxx == (fb->width - 1) && + fb->draw_extent.maxy == (fb->height - 1); +} + static inline bool pan_clean_tile_write_rt_enabled(struct pan_clean_tile clean_tile, unsigned index) From bda7f8572e8a260f804d0a952a655539c9ab4108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Tue, 10 Feb 2026 11:12:21 +0100 Subject: [PATCH 03/18] pan/crc: Check AFBC renderblock size on v5 and v6 too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arch v5 and v6 should test the AFBC render block size too. In the non-AFBC case, there's no need to check for the tile size which is checked earlier by the caller. Signed-off-by: Loïc Molinari --- src/panfrost/lib/pan_desc.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index 4487f9da57c..15932d3269c 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -100,7 +100,7 @@ renderblock_fits_in_single_pass(const struct pan_image_view *view, uint64_t mod = pref.image->props.modifier; if (!drm_is_afbc(mod)) - return tile_size >= 16 * 16; + return true; struct pan_image_block_size renderblk_sz = pan_afbc_renderblock_size(mod); return tile_size >= renderblk_sz.width * renderblk_sz.height; @@ -123,7 +123,8 @@ GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) #if PAN_ARCH <= 6 if (fb->rt_count == 1 && fb->rts[0].view && !fb->rts[0].discard && - pan_image_view_has_crc(fb->rts[0].view)) + pan_image_view_has_crc(fb->rts[0].view) && + renderblock_fits_in_single_pass(fb->rts[0].view, tile_size)) return 0; return -1; @@ -133,10 +134,8 @@ GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) for (unsigned i = 0; i < fb->rt_count; i++) { if (!fb->rts[i].view || fb->rts[i].discard || - !pan_image_view_has_crc(fb->rts[i].view)) - continue; - - if (!renderblock_fits_in_single_pass(fb->rts[i].view, tile_size)) + !pan_image_view_has_crc(fb->rts[i].view) || + !renderblock_fits_in_single_pass(fb->rts[i].view, tile_size)) continue; bool valid = *(fb->rts[i].crc_valid); From e39e303e23f1f4e8f09c06d06699ec2340baa6c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Mon, 9 Feb 2026 15:08:49 +0100 Subject: [PATCH 04/18] pan/crc: Check CRC buffer validity and coverage on v5 and v6 too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CRC RT selection for v5 and v6 (v4 isn't supported) currently returns 0 (instead of -1) as long as the CRC buffer is usable but without checking its validity like it's done for v7+. While it doesn't incorrectly enable Transaction Elimination, it uselessly makes dependent CRC code paths taken. Signed-off-by: Loïc Molinari --- src/panfrost/lib/pan_desc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index 15932d3269c..090a47bc3f0 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -124,7 +124,8 @@ GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) #if PAN_ARCH <= 6 if (fb->rt_count == 1 && fb->rts[0].view && !fb->rts[0].discard && pan_image_view_has_crc(fb->rts[0].view) && - renderblock_fits_in_single_pass(fb->rts[0].view, tile_size)) + renderblock_fits_in_single_pass(fb->rts[0].view, tile_size) && + (*(fb->rts[0].crc_valid) || pan_fb_is_fully_covered(fb))) return 0; return -1; From f2a678ed45dcc35eaeaa815cf46ae8152762897c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Tue, 10 Feb 2026 12:04:16 +0100 Subject: [PATCH 05/18] pan/crc: Simplify CRC buffer selection logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit doesn't really change the selection logic but tries to make the reasoning more straightforward and prepare for future commits where the CRC state will be cached. A usable RT must pass a few conditional checks like the availability of a CRC buffer. A selected RT must be usable and either have a valid CRC buffer or be fully covered. In the MRT case, the first usable RT with a valid CRC buffer is selected. If no RT has a CRC buffer initialized, then the first usable RT is selected at the condition it's fully covered. Signed-off-by: Loïc Molinari --- src/panfrost/lib/pan_desc.c | 38 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index 090a47bc3f0..e9f43917bae 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -109,6 +109,8 @@ renderblock_fits_in_single_pass(const struct pan_image_view *view, int GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) { + int best_rt = -1; + /* Disable CRC when the tile size is smaller than 16x16. In the hardware, * CRC tiles are the same size as the tiles of the framebuffer. However, * our code only handles 16x16 tiles. Therefore under the current @@ -119,42 +121,40 @@ GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) * Restricting CRC to 16x16 should work in practice. */ if (tile_size < 16 * 16) - return -1; + return best_rt; #if PAN_ARCH <= 6 if (fb->rt_count == 1 && fb->rts[0].view && !fb->rts[0].discard && pan_image_view_has_crc(fb->rts[0].view) && - renderblock_fits_in_single_pass(fb->rts[0].view, tile_size) && - (*(fb->rts[0].crc_valid) || pan_fb_is_fully_covered(fb))) - return 0; - - return -1; + renderblock_fits_in_single_pass(fb->rts[0].view, tile_size)) + best_rt = 0; #else - bool best_rt_valid = false; - int best_rt = -1; - for (unsigned i = 0; i < fb->rt_count; i++) { + /* Skip unusable RT. */ if (!fb->rts[i].view || fb->rts[i].discard || !pan_image_view_has_crc(fb->rts[i].view) || !renderblock_fits_in_single_pass(fb->rts[i].view, tile_size)) continue; - bool valid = *(fb->rts[i].crc_valid); - bool full = pan_fb_is_fully_covered(fb); - if (!full && !valid) - continue; - - if (best_rt < 0 || (valid && !best_rt_valid)) { + /* Select the first RT with a valid CRC buffer. */ + if (*fb->rts[i].crc_valid) { best_rt = i; - best_rt_valid = valid; + break; } - if (valid) - break; + /* Store the first usable RT otherwise. */ + if (best_rt == -1) + best_rt = i; } +#endif + + /* The selected RT must be fully covered for now in order to correctly + * initialize the CRC buffer. */ + if (best_rt != -1 && !*fb->rts[best_rt].crc_valid && + !pan_fb_is_fully_covered(fb)) + best_rt = -1; return best_rt; -#endif } static enum mali_zs_format From 00f4ed96436763221a078d523fb3eb5fb0c5958f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Tue, 10 Feb 2026 12:23:53 +0100 Subject: [PATCH 06/18] pan/crc: Use RT selection loop in single RT case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single RT case can now use a common code path. Signed-off-by: Loïc Molinari --- src/panfrost/lib/pan_desc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index e9f43917bae..ea2a65c3395 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -124,11 +124,10 @@ GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) return best_rt; #if PAN_ARCH <= 6 - if (fb->rt_count == 1 && fb->rts[0].view && !fb->rts[0].discard && - pan_image_view_has_crc(fb->rts[0].view) && - renderblock_fits_in_single_pass(fb->rts[0].view, tile_size)) - best_rt = 0; -#else + if (fb->rt_count > 1) + return best_rt; +#endif + for (unsigned i = 0; i < fb->rt_count; i++) { /* Skip unusable RT. */ if (!fb->rts[i].view || fb->rts[i].discard || @@ -146,7 +145,6 @@ GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) if (best_rt == -1) best_rt = i; } -#endif /* The selected RT must be fully covered for now in order to correctly * initialize the CRC buffer. */ From dbd3f9b6add05d8f553135770e45dd7d9c6e3c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Wed, 11 Feb 2026 08:31:39 +0100 Subject: [PATCH 07/18] pan/crc: Check CRC requirements in dedicated function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Loïc Molinari --- src/gallium/drivers/panfrost/pan_fb_preload.c | 2 +- src/panfrost/lib/pan_desc.c | 31 ++++++++++++------- src/panfrost/lib/pan_desc.h | 2 +- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/gallium/drivers/panfrost/pan_fb_preload.c b/src/gallium/drivers/panfrost/pan_fb_preload.c index e534a69c8d1..46bf5388d9f 100644 --- a/src/gallium/drivers/panfrost/pan_fb_preload.c +++ b/src/gallium/drivers/panfrost/pan_fb_preload.c @@ -1197,7 +1197,7 @@ pan_preload_emit_pre_frame_dcd(struct pan_fb_preload_cache *cache, * write even clean tiles to make sure CRC data is updated. */ if (crc_rt >= 0) { bool *valid = fb->rts[crc_rt].crc_valid; - bool full = pan_fb_is_fully_covered(fb); + bool full = pan_fb_info_is_fully_covered(fb); if (full && !(*valid)) always_write = true; diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index ea2a65c3395..1d97609f012 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -93,17 +93,26 @@ pan_warn_on_afbc_reverse_issue_order(const struct pan_attachment_info *att, #endif static bool -renderblock_fits_in_single_pass(const struct pan_image_view *view, - unsigned tile_size) +pan_fb_color_attachment_should_crc(const struct pan_fb_color_attachment *rt, + unsigned tile_size) { - const struct pan_image_plane_ref pref = pan_image_view_get_first_plane(view); - uint64_t mod = pref.image->props.modifier; + uint64_t mod; + struct pan_image_block_size renderblk_sz; + + if (!rt->view || rt->discard || !pan_image_view_has_crc(rt->view)) + return false; + + mod = pan_image_view_get_first_plane(rt->view).image->props.modifier; if (!drm_is_afbc(mod)) return true; - struct pan_image_block_size renderblk_sz = pan_afbc_renderblock_size(mod); - return tile_size >= renderblk_sz.width * renderblk_sz.height; + /* AFBC render block size must fit in a single pass. */ + renderblk_sz = pan_afbc_renderblock_size(mod); + if (tile_size < renderblk_sz.width * renderblk_sz.height) + return false; + + return true; } int @@ -129,10 +138,8 @@ GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) #endif for (unsigned i = 0; i < fb->rt_count; i++) { - /* Skip unusable RT. */ - if (!fb->rts[i].view || fb->rts[i].discard || - !pan_image_view_has_crc(fb->rts[i].view) || - !renderblock_fits_in_single_pass(fb->rts[i].view, tile_size)) + /* Skip unusable RTs. */ + if (!pan_fb_color_attachment_should_crc(&fb->rts[i], tile_size)) continue; /* Select the first RT with a valid CRC buffer. */ @@ -149,7 +156,7 @@ GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) /* The selected RT must be fully covered for now in order to correctly * initialize the CRC buffer. */ if (best_rt != -1 && !*fb->rts[best_rt].crc_valid && - !pan_fb_is_fully_covered(fb)) + !pan_fb_info_is_fully_covered(fb)) best_rt = -1; return best_rt; @@ -1252,7 +1259,7 @@ GENX(pan_emit_fbd)(const struct pan_fb_info *fb, unsigned layer_idx, if (crc_rt >= 0) { bool *valid = fb->rts[crc_rt].crc_valid; - bool full = pan_fb_is_fully_covered(fb); + bool full = pan_fb_info_is_fully_covered(fb); /* If the CRC was valid it stays valid, if it wasn't, we must ensure * the render operation covers the full frame, and clean tiles are diff --git a/src/panfrost/lib/pan_desc.h b/src/panfrost/lib/pan_desc.h index a85ffbcc013..fddcfbae78a 100644 --- a/src/panfrost/lib/pan_desc.h +++ b/src/panfrost/lib/pan_desc.h @@ -163,7 +163,7 @@ struct pan_clean_tile { }; static inline bool -pan_fb_is_fully_covered(const struct pan_fb_info *fb) +pan_fb_info_is_fully_covered(const struct pan_fb_info *fb) { return !fb->draw_extent.minx && !fb->draw_extent.miny && From 8c7adaf1eaafe1320ebf5c2ee8450aaa086b0c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Wed, 11 Feb 2026 08:49:20 +0100 Subject: [PATCH 08/18] pan/crc: Disallow CRC on sparse AFBC images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AFBC-P images are read only. Signed-off-by: Loïc Molinari --- src/panfrost/lib/pan_desc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index 1d97609f012..84591545736 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -107,6 +107,10 @@ pan_fb_color_attachment_should_crc(const struct pan_fb_color_attachment *rt, if (!drm_is_afbc(mod)) return true; + /* AFBC-P images are read only. */ + if (!(mod & AFBC_FORMAT_MOD_SPARSE)) + return false; + /* AFBC render block size must fit in a single pass. */ renderblk_sz = pan_afbc_renderblock_size(mod); if (tile_size < renderblk_sz.width * renderblk_sz.height) From 789ae79688c9413185f681aed3dcfb7098e5d06c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Tue, 10 Feb 2026 12:36:29 +0100 Subject: [PATCH 09/18] pan/crc: Move CRC selection function closer to caller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Loïc Molinari --- src/panfrost/lib/pan_desc.c | 148 ++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index 84591545736..921afbf4698 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -92,80 +92,6 @@ pan_warn_on_afbc_reverse_issue_order(const struct pan_attachment_info *att, } #endif -static bool -pan_fb_color_attachment_should_crc(const struct pan_fb_color_attachment *rt, - unsigned tile_size) -{ - uint64_t mod; - struct pan_image_block_size renderblk_sz; - - if (!rt->view || rt->discard || !pan_image_view_has_crc(rt->view)) - return false; - - mod = pan_image_view_get_first_plane(rt->view).image->props.modifier; - - if (!drm_is_afbc(mod)) - return true; - - /* AFBC-P images are read only. */ - if (!(mod & AFBC_FORMAT_MOD_SPARSE)) - return false; - - /* AFBC render block size must fit in a single pass. */ - renderblk_sz = pan_afbc_renderblock_size(mod); - if (tile_size < renderblk_sz.width * renderblk_sz.height) - return false; - - return true; -} - -int -GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) -{ - int best_rt = -1; - - /* Disable CRC when the tile size is smaller than 16x16. In the hardware, - * CRC tiles are the same size as the tiles of the framebuffer. However, - * our code only handles 16x16 tiles. Therefore under the current - * implementation, we must disable CRC when 16x16 tiles are not used. - * - * This may hurt performance. However, smaller tile sizes are rare, and - * CRCs are more expensive at smaller tile sizes, reducing the benefit. - * Restricting CRC to 16x16 should work in practice. - */ - if (tile_size < 16 * 16) - return best_rt; - -#if PAN_ARCH <= 6 - if (fb->rt_count > 1) - return best_rt; -#endif - - for (unsigned i = 0; i < fb->rt_count; i++) { - /* Skip unusable RTs. */ - if (!pan_fb_color_attachment_should_crc(&fb->rts[i], tile_size)) - continue; - - /* Select the first RT with a valid CRC buffer. */ - if (*fb->rts[i].crc_valid) { - best_rt = i; - break; - } - - /* Store the first usable RT otherwise. */ - if (best_rt == -1) - best_rt = i; - } - - /* The selected RT must be fully covered for now in order to correctly - * initialize the CRC buffer. */ - if (best_rt != -1 && !*fb->rts[best_rt].crc_valid && - !pan_fb_info_is_fully_covered(fb)) - best_rt = -1; - - return best_rt; -} - static enum mali_zs_format translate_zs_format(enum pipe_format in) { @@ -1092,6 +1018,80 @@ pan_fix_frame_shader_mode(enum mali_pre_post_frame_shader_mode mode, } #endif +static bool +pan_fb_color_attachment_should_crc(const struct pan_fb_color_attachment *rt, + unsigned tile_size) +{ + uint64_t mod; + struct pan_image_block_size renderblk_sz; + + if (!rt->view || rt->discard || !pan_image_view_has_crc(rt->view)) + return false; + + mod = pan_image_view_get_first_plane(rt->view).image->props.modifier; + + if (!drm_is_afbc(mod)) + return true; + + /* AFBC-P images are read only. */ + if (!(mod & AFBC_FORMAT_MOD_SPARSE)) + return false; + + /* AFBC render block size must fit in a single pass. */ + renderblk_sz = pan_afbc_renderblock_size(mod); + if (tile_size < renderblk_sz.width * renderblk_sz.height) + return false; + + return true; +} + +int +GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) +{ + int best_rt = -1; + + /* Disable CRC when the tile size is smaller than 16x16. In the hardware, + * CRC tiles are the same size as the tiles of the framebuffer. However, + * our code only handles 16x16 tiles. Therefore under the current + * implementation, we must disable CRC when 16x16 tiles are not used. + * + * This may hurt performance. However, smaller tile sizes are rare, and + * CRCs are more expensive at smaller tile sizes, reducing the benefit. + * Restricting CRC to 16x16 should work in practice. + */ + if (tile_size < 16 * 16) + return best_rt; + +#if PAN_ARCH <= 6 + if (fb->rt_count > 1) + return best_rt; +#endif + + for (unsigned i = 0; i < fb->rt_count; i++) { + /* Skip unusable RTs. */ + if (!pan_fb_color_attachment_should_crc(&fb->rts[i], tile_size)) + continue; + + /* Select the first RT with a valid CRC buffer. */ + if (*fb->rts[i].crc_valid) { + best_rt = i; + break; + } + + /* Store the first usable RT otherwise. */ + if (best_rt == -1) + best_rt = i; + } + + /* The selected RT must be fully covered for now in order to correctly + * initialize the CRC buffer. */ + if (best_rt != -1 && !*fb->rts[best_rt].crc_valid && + !pan_fb_info_is_fully_covered(fb)) + best_rt = -1; + + return best_rt; +} + /* Clean tiles must be written back for AFBC buffers (color, z/s) when either * one of the effective tile size dimension is smaller than the superblock * dimension. From d911ef7c9d3bc29ad1adae12ee132902f4a0541d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Mon, 9 Feb 2026 15:35:02 +0100 Subject: [PATCH 10/18] pan/crc: Simplify CRC buffer initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Transaction Elimination on a RT is disabled until there's a full frame render with all tiles forcefully written back. This is currently done by letting the Gallium driver track states and fix up FB preload by disabling clean_fragment_write on the pre-frame DCD and by setting the pre-frame mode to "always" (instead of "intersect"). This commit forces the write-back of all the tiles by setting clean_tile_write_enable on the FBD instead. This simplifies the code and removes most of the CRC state tracking from the Gallium driver. Signed-off-by: Loïc Molinari --- src/gallium/drivers/panfrost/pan_fb_preload.c | 29 ++++--------------- src/panfrost/lib/pan_desc.c | 27 ++++++++++++----- src/panfrost/lib/pan_desc.h | 2 -- 3 files changed, 24 insertions(+), 34 deletions(-) diff --git a/src/gallium/drivers/panfrost/pan_fb_preload.c b/src/gallium/drivers/panfrost/pan_fb_preload.c index 46bf5388d9f..73d11412d58 100644 --- a/src/gallium/drivers/panfrost/pan_fb_preload.c +++ b/src/gallium/drivers/panfrost/pan_fb_preload.c @@ -1018,8 +1018,7 @@ pan_preload_emit_viewport(struct pan_pool *pool, uint16_t minx, uint16_t miny, static void pan_preload_emit_dcd(struct pan_fb_preload_cache *cache, struct pan_pool *pool, struct pan_fb_info *fb, bool zs, uint64_t coordinates, - uint64_t tsd, struct mali_draw_packed *out, - bool always_write) + uint64_t tsd, struct mali_draw_packed *out) { unsigned tex_count = 0; uint64_t textures = pan_preload_emit_textures(pool, fb, zs, &tex_count); @@ -1031,7 +1030,7 @@ pan_preload_emit_dcd(struct pan_fb_preload_cache *cache, struct pan_pool *pool, /* Tiles updated by preload shaders are still considered clean (separate * for colour and Z/S), allowing us to suppress unnecessary writeback */ - UNUSED bool clean_fragment_write = !always_write; + UNUSED bool clean_fragment_write = true; /* Image view used when patching stencil formats for combined * depth/stencil preloads. @@ -1186,25 +1185,8 @@ pan_preload_emit_pre_frame_dcd(struct pan_fb_preload_cache *cache, void *dcd = fb->bifrost.pre_post.dcds.cpu + (dcd_idx * pan_size(DRAW)); - /* We only use crc_rt to determine whether to force writes for updating - * the CRCs, so use a conservative tile size (16x16). - */ - int crc_rt = GENX(pan_select_crc_rt)(fb, 16 * 16); + pan_preload_emit_dcd(cache, desc_pool, fb, zs, coords, tsd, dcd); - bool always_write = false; - - /* If CRC data is currently invalid and this batch will make it valid, - * write even clean tiles to make sure CRC data is updated. */ - if (crc_rt >= 0) { - bool *valid = fb->rts[crc_rt].crc_valid; - bool full = pan_fb_info_is_fully_covered(fb); - - if (full && !(*valid)) - always_write = true; - } - - pan_preload_emit_dcd(cache, desc_pool, fb, zs, coords, tsd, dcd, - always_write); if (zs) { enum pipe_format fmt = fb->zs.view.zs ? fb->zs.view.zs->planes[0].image->props.format @@ -1264,8 +1246,7 @@ pan_preload_emit_pre_frame_dcd(struct pan_fb_preload_cache *cache, #endif } else { fb->bifrost.pre_post.modes[dcd_idx] = - always_write ? MALI_PRE_POST_FRAME_SHADER_MODE_ALWAYS - : MALI_PRE_POST_FRAME_SHADER_MODE_INTERSECT; + MALI_PRE_POST_FRAME_SHADER_MODE_INTERSECT; } } #else @@ -1280,7 +1261,7 @@ pan_preload_emit_tiler_job(struct pan_fb_preload_cache *cache, return (struct pan_ptr){0}; pan_preload_emit_dcd(cache, desc_pool, fb, zs, coords, tsd, - pan_section_ptr(job.cpu, TILER_JOB, DRAW), false); + pan_section_ptr(job.cpu, TILER_JOB, DRAW)); pan_section_pack(job.cpu, TILER_JOB, PRIMITIVE, cfg) { cfg.draw_mode = MALI_DRAW_MODE_TRIANGLE_STRIP; diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index 921afbf4698..cc9dd8e288e 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -1045,8 +1045,8 @@ pan_fb_color_attachment_should_crc(const struct pan_fb_color_attachment *rt, return true; } -int -GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) +static int +pan_select_crc_rt(const struct pan_fb_info *fb) { int best_rt = -1; @@ -1059,7 +1059,7 @@ GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) * CRCs are more expensive at smaller tile sizes, reducing the benefit. * Restricting CRC to 16x16 should work in practice. */ - if (tile_size < 16 * 16) + if (fb->tile_size < 16 * 16) return best_rt; #if PAN_ARCH <= 6 @@ -1069,7 +1069,7 @@ GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size) for (unsigned i = 0; i < fb->rt_count; i++) { /* Skip unusable RTs. */ - if (!pan_fb_color_attachment_should_crc(&fb->rts[i], tile_size)) + if (!pan_fb_color_attachment_should_crc(&fb->rts[i], fb->tile_size)) continue; /* Select the first RT with a valid CRC buffer. */ @@ -1118,8 +1118,18 @@ GENX(pan_force_clean_write_on)(const struct pan_image *image, #endif } +static bool +pan_force_clean_write_crc(const struct pan_fb_info *fb, int index, int crc_rt) +{ + bool match = index == crc_rt; + bool valid = *(fb->rts[index].crc_valid); + bool full = pan_fb_info_is_fully_covered(fb); + + return match && !valid && full; +} + static struct pan_clean_tile -pan_get_clean_tile_info(const struct pan_fb_info *fb) +pan_get_clean_tile_info(const struct pan_fb_info *fb, int crc_rt) { struct pan_clean_tile clean_tile = { 0, }; const struct pan_image *img; @@ -1134,7 +1144,8 @@ pan_get_clean_tile_info(const struct pan_fb_info *fb) img = fb->rts[i].view ? pan_image_view_get_color_plane(fb->rts[i].view).image : NULL; if (fb->rts[i].clear || - GENX(pan_force_clean_write_on)(img, fb->tile_size)) + GENX(pan_force_clean_write_on)(img, fb->tile_size) || + pan_force_clean_write_crc(fb, i, crc_rt)) clean_tile.write_rt_mask |= 1 << i; } @@ -1195,9 +1206,9 @@ GENX(pan_emit_fbd)(const struct pan_fb_info *fb, unsigned layer_idx, GENX(pan_emit_tls)(tls, pan_section_ptr(fbd, FRAMEBUFFER, LOCAL_STORAGE)); #endif - int crc_rt = GENX(pan_select_crc_rt)(fb, fb->tile_size); + int crc_rt = pan_select_crc_rt(fb); bool has_zs_crc_ext = (fb->zs.view.zs || fb->zs.view.s || crc_rt >= 0); - struct pan_clean_tile clean_tile = pan_get_clean_tile_info(fb); + struct pan_clean_tile clean_tile = pan_get_clean_tile_info(fb, crc_rt); pan_section_pack(fbd, FRAMEBUFFER, PARAMETERS, cfg) { #if PAN_ARCH >= 6 diff --git a/src/panfrost/lib/pan_desc.h b/src/panfrost/lib/pan_desc.h index fddcfbae78a..08ac5c588b8 100644 --- a/src/panfrost/lib/pan_desc.h +++ b/src/panfrost/lib/pan_desc.h @@ -303,8 +303,6 @@ bool GENX(pan_force_clean_write_on)(const struct pan_image *image, void GENX(pan_emit_tls)(const struct pan_tls_info *info, struct mali_local_storage_packed *out); -int GENX(pan_select_crc_rt)(const struct pan_fb_info *fb, unsigned tile_size); - struct pan_attachment_info { const struct pan_image_view *iview; unsigned layer_or_z_slice; From 32229dba83aee53bbcd360eb6a3078b4758fb3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Tue, 10 Feb 2026 09:38:41 +0100 Subject: [PATCH 11/18] pan/crc: Cache temporary CRC info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Retrieve and cache temporary CRC info once at the beginning of pan_emit_fbd(). This makes CRC info retrieval more localized and avoids duplication. Signed-off-by: Loïc Molinari --- src/panfrost/lib/pan_desc.c | 150 ++++++++++++++++++++---------------- src/panfrost/lib/pan_desc.h | 18 +++++ 2 files changed, 102 insertions(+), 66 deletions(-) diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index cc9dd8e288e..23c7ee8ba1c 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -387,30 +387,28 @@ GENX(pan_emit_interleaved_64k_zs_attachment)(const struct pan_attachment_info *a #endif static void -pan_prepare_crc(const struct pan_fb_info *fb, int rt_crc, - struct MALI_CRC *crc) +pan_emit_crc(const struct pan_fb_info *fb, struct pan_crc *crc, + struct MALI_CRC *cfg) { - if (rt_crc < 0) + if (!pan_crc_is_enabled(crc)) return; - assert(rt_crc < fb->rt_count); - - const struct pan_image_view *rt = fb->rts[rt_crc].view; + const struct pan_image_view *rt = fb->rts[crc->index].view; const struct pan_image_plane_ref pref = pan_image_view_get_color_plane(rt); const struct pan_image *image = pref.image; const struct pan_image_plane *plane = image->planes[pref.plane_idx]; const struct pan_image_slice_layout *slice = &plane->layout.slices[rt->first_level]; - crc->base = plane->base + slice->crc.offset_B; - crc->row_stride = slice->crc.stride_B; + cfg->base = plane->base + slice->crc.offset_B; + cfg->row_stride = slice->crc.stride_B; #if PAN_ARCH >= 7 - crc->render_target = rt_crc; + cfg->render_target = crc->index; - if (fb->rts[rt_crc].clear) { - uint32_t clear_val = fb->rts[rt_crc].clear_value[0]; - crc->clear_color = clear_val | 0xc000000000000000 | + if (fb->rts[crc->index].clear) { + uint32_t clear_val = fb->rts[crc->index].clear_value[0]; + cfg->clear_color = clear_val | 0xc000000000000000 | (((uint64_t)clear_val & 0xffff) << 32); } #endif @@ -418,14 +416,13 @@ pan_prepare_crc(const struct pan_fb_info *fb, int rt_crc, static void pan_emit_zs_crc_ext(const struct pan_fb_info *fb, unsigned layer_idx, - int rt_crc, struct mali_zs_crc_extension_packed *zs_crc_ext, - struct pan_clean_tile clean_tile) + struct pan_crc *crc, struct pan_clean_tile clean_tile) { struct mali_zs_crc_extension_packed desc; pan_pack(&desc, ZS_CRC_EXTENSION, cfg) { - pan_prepare_crc(fb, rt_crc, &cfg.crc); + pan_emit_crc(fb, crc, &cfg.crc); #if PAN_ARCH == 5 cfg.zs.clean_pixel_write_enable = pan_clean_tile_write_zs_enabled(clean_tile); @@ -1050,18 +1047,6 @@ pan_select_crc_rt(const struct pan_fb_info *fb) { int best_rt = -1; - /* Disable CRC when the tile size is smaller than 16x16. In the hardware, - * CRC tiles are the same size as the tiles of the framebuffer. However, - * our code only handles 16x16 tiles. Therefore under the current - * implementation, we must disable CRC when 16x16 tiles are not used. - * - * This may hurt performance. However, smaller tile sizes are rare, and - * CRCs are more expensive at smaller tile sizes, reducing the benefit. - * Restricting CRC to 16x16 should work in practice. - */ - if (fb->tile_size < 16 * 16) - return best_rt; - #if PAN_ARCH <= 6 if (fb->rt_count > 1) return best_rt; @@ -1083,15 +1068,69 @@ pan_select_crc_rt(const struct pan_fb_info *fb) best_rt = i; } - /* The selected RT must be fully covered for now in order to correctly - * initialize the CRC buffer. */ - if (best_rt != -1 && !*fb->rts[best_rt].crc_valid && - !pan_fb_info_is_fully_covered(fb)) - best_rt = -1; - return best_rt; } +static void +pan_crc_enable(struct pan_crc *crc) +{ + crc->read = true; + crc->write = true; +} + +/* Take advantage of a full frame draw to initialize the CRC buffer by + * forcefully writing back all the tiles and flush the CRC values. Drawback + * is it only works on full frames. */ +static void +pan_crc_maybe_enable_flushed(struct pan_crc *crc, + const struct pan_fb_info *fb) +{ + if (!pan_fb_info_is_fully_covered(fb)) + return; + + crc->write = true; + crc->force_clean_tile_write = true; +} + +static struct pan_crc +pan_get_crc_info(const struct pan_fb_info *fb) +{ + struct pan_crc crc = { .index = -1, }; + const struct pan_fb_color_attachment *rt; + + /* Disable TE when the tile size is smaller than 16x16. In the hardware, + * CRC tiles are the same size as the tiles of the framebuffer. However, + * our code only handles 16x16 tiles. Therefore under the current + * implementation, we must disable TE when 16x16 tiles are not used. This + * may hurt performance. However, smaller tile sizes are rare, and CRCs are + * more expensive at smaller tile sizes, reducing the benefit. Restricting + * CRC to 16x16 should work in practice. */ + if (fb->tile_size < 16 * 16) + goto skip; + + crc.index = pan_select_crc_rt(fb); + if (crc.index == -1) + goto skip; + + rt = &fb->rts[crc.index]; + + /* Transaction Elimination. */ + if (*rt->crc_valid) { + pan_crc_enable(&crc); + } else { + pan_crc_maybe_enable_flushed(&crc, fb); + *rt->crc_valid = true; + } + + skip: + /* Flag CRC buffer states of unselected RTs as invalid. */ + for (unsigned i = 0; i < fb->rt_count; i++) + if (i != crc.index && fb->rts[i].crc_valid) + *fb->rts[i].crc_valid = false; + + return crc; +} + /* Clean tiles must be written back for AFBC buffers (color, z/s) when either * one of the effective tile size dimension is smaller than the superblock * dimension. @@ -1119,17 +1158,13 @@ GENX(pan_force_clean_write_on)(const struct pan_image *image, } static bool -pan_force_clean_write_crc(const struct pan_fb_info *fb, int index, int crc_rt) +pan_force_clean_write_crc(struct pan_crc *crc, int index) { - bool match = index == crc_rt; - bool valid = *(fb->rts[index].crc_valid); - bool full = pan_fb_info_is_fully_covered(fb); - - return match && !valid && full; + return index == crc->index && crc->force_clean_tile_write; } static struct pan_clean_tile -pan_get_clean_tile_info(const struct pan_fb_info *fb, int crc_rt) +pan_get_clean_tile_info(const struct pan_fb_info *fb, struct pan_crc *crc) { struct pan_clean_tile clean_tile = { 0, }; const struct pan_image *img; @@ -1145,7 +1180,7 @@ pan_get_clean_tile_info(const struct pan_fb_info *fb, int crc_rt) pan_image_view_get_color_plane(fb->rts[i].view).image : NULL; if (fb->rts[i].clear || GENX(pan_force_clean_write_on)(img, fb->tile_size) || - pan_force_clean_write_crc(fb, i, crc_rt)) + pan_force_clean_write_crc(crc, i)) clean_tile.write_rt_mask |= 1 << i; } @@ -1206,9 +1241,10 @@ GENX(pan_emit_fbd)(const struct pan_fb_info *fb, unsigned layer_idx, GENX(pan_emit_tls)(tls, pan_section_ptr(fbd, FRAMEBUFFER, LOCAL_STORAGE)); #endif - int crc_rt = pan_select_crc_rt(fb); - bool has_zs_crc_ext = (fb->zs.view.zs || fb->zs.view.s || crc_rt >= 0); - struct pan_clean_tile clean_tile = pan_get_clean_tile_info(fb, crc_rt); + struct pan_crc crc = pan_get_crc_info(fb); + struct pan_clean_tile clean_tile = pan_get_clean_tile_info(fb, &crc); + bool has_zs_crc_ext = fb->zs.view.zs || fb->zs.view.s || + pan_crc_is_enabled(&crc); pan_section_pack(fbd, FRAMEBUFFER, PARAMETERS, cfg) { #if PAN_ARCH >= 6 @@ -1272,24 +1308,9 @@ GENX(pan_emit_fbd)(const struct pan_fb_info *fb, unsigned layer_idx, cfg.s_write_enable = (fb->zs.view.s && !fb->zs.discard.s); cfg.has_zs_crc_extension = has_zs_crc_ext; - if (crc_rt >= 0) { - bool *valid = fb->rts[crc_rt].crc_valid; - bool full = pan_fb_info_is_fully_covered(fb); - - /* If the CRC was valid it stays valid, if it wasn't, we must ensure - * the render operation covers the full frame, and clean tiles are - * pushed to memory. */ - bool new_valid = *valid | - (full && pan_clean_tile_write_rt_enabled(clean_tile, crc_rt)); - - cfg.crc_read_enable = *valid; - - /* If the data is currently invalid, still write CRC - * data if we are doing a full write, so that it is - * valid for next time. */ - cfg.crc_write_enable = new_valid; - - *valid = new_valid; + if (pan_crc_is_enabled(&crc)) { + cfg.crc_read_enable = crc.read; + cfg.crc_write_enable = crc.write; } #if PAN_ARCH >= 9 @@ -1340,7 +1361,7 @@ GENX(pan_emit_fbd)(const struct pan_fb_info *fb, unsigned layer_idx, struct mali_zs_crc_extension_packed *zs_crc_ext = out + pan_size(FRAMEBUFFER); - pan_emit_zs_crc_ext(fb, layer_idx, crc_rt, zs_crc_ext, clean_tile); + pan_emit_zs_crc_ext(fb, layer_idx, zs_crc_ext, &crc, clean_tile); rtd += pan_size(ZS_CRC_EXTENSION); } @@ -1355,9 +1376,6 @@ GENX(pan_emit_fbd)(const struct pan_fb_info *fb, unsigned layer_idx, cbuf_offset += pan_bytes_per_pixel_tib(fb->rts[i].view->format) * fb->tile_size * pan_image_view_get_nr_samples(fb->rts[i].view); - - if (i != crc_rt && fb->rts[i].crc_valid != NULL) - *(fb->rts[i].crc_valid) = false; } struct mali_framebuffer_pointer_packed tag; diff --git a/src/panfrost/lib/pan_desc.h b/src/panfrost/lib/pan_desc.h index 08ac5c588b8..2772d266dc1 100644 --- a/src/panfrost/lib/pan_desc.h +++ b/src/panfrost/lib/pan_desc.h @@ -154,6 +154,18 @@ struct pan_fb_info { bool pls_enabled; }; +struct pan_crc { + /* Selected RT index (8 max), -1 if none. */ + int8_t index; + + /* Transaction Elimination flags */ + bool read : 1; + bool write : 1; + + /* Force clean writes for CRC buffer init */ + bool force_clean_tile_write : 1; +}; + struct pan_clean_tile { /* clean_tile_write_enable mask on the 8 color attachments. */ uint8_t write_rt_mask; @@ -171,6 +183,12 @@ pan_fb_info_is_fully_covered(const struct pan_fb_info *fb) fb->draw_extent.maxy == (fb->height - 1); } +static inline bool +pan_crc_is_enabled(struct pan_crc *crc) +{ + return crc->index != -1; +} + static inline bool pan_clean_tile_write_rt_enabled(struct pan_clean_tile clean_tile, unsigned index) From 9960843c131f9c392e0062c1c22d507c0d563f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Wed, 11 Feb 2026 06:54:06 +0100 Subject: [PATCH 12/18] pan/crc: allow setting a NULL pointer to the CRC validity state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Loïc Molinari --- src/panfrost/lib/pan_desc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index 23c7ee8ba1c..3c82d184e97 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -1022,7 +1022,8 @@ pan_fb_color_attachment_should_crc(const struct pan_fb_color_attachment *rt, uint64_t mod; struct pan_image_block_size renderblk_sz; - if (!rt->view || rt->discard || !pan_image_view_has_crc(rt->view)) + if (!rt->view || rt->discard || !rt->crc_valid || + !pan_image_view_has_crc(rt->view)) return false; mod = pan_image_view_get_first_plane(rt->view).image->props.modifier; From c566aaed2ebabd38775dff433102c8606d6b37f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Wed, 11 Feb 2026 09:13:15 +0100 Subject: [PATCH 13/18] pan/crc: Store CRC state in a struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the pan_crc_state structure and use it to store CRC state. The struct only has a valid boolean for now but will be extended later. This removes some explicit dereferencing, allows to wrap state handling inside functions and helps readability. Signed-off-by: Loïc Molinari --- src/gallium/drivers/panfrost/pan_job.c | 2 +- src/gallium/drivers/panfrost/pan_resource.c | 4 ++-- src/gallium/drivers/panfrost/pan_resource.h | 7 +++---- src/panfrost/lib/pan_desc.c | 16 ++++++++-------- src/panfrost/lib/pan_desc.h | 13 ++++++++++++- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/gallium/drivers/panfrost/pan_job.c b/src/gallium/drivers/panfrost/pan_job.c index 80fb5c2eb16..3d7bea33f50 100644 --- a/src/gallium/drivers/panfrost/pan_job.c +++ b/src/gallium/drivers/panfrost/pan_job.c @@ -531,7 +531,7 @@ panfrost_batch_to_fb_info(const struct panfrost_batch *batch, rts[i].nr_samples = surf->nr_samples ?: MAX2(surf->texture->nr_samples, 1); memcpy(rts[i].swizzle, id_swz, sizeof(rts[i].swizzle)); - fb->rts[i].crc_valid = &prsrc->valid.crc; + fb->rts[i].crc_state = &prsrc->crc_state; fb->rts[i].view = &rts[i]; /* Preload if the RT is read or updated */ diff --git a/src/gallium/drivers/panfrost/pan_resource.c b/src/gallium/drivers/panfrost/pan_resource.c index bc1ca6be1ac..7a004320a0e 100644 --- a/src/gallium/drivers/panfrost/pan_resource.c +++ b/src/gallium/drivers/panfrost/pan_resource.c @@ -2206,7 +2206,7 @@ pan_resource_afbcp_commit(struct panfrost_context *ctx, prsrc->plane.layout.data_size_B = prsrc->afbcp->size; prsrc->plane.base = prsrc->afbcp->packed_bo->ptr.gpu; prsrc->image.props.crc = false; - prsrc->valid.crc = false; + pan_crc_state_invalidate(&prsrc->crc_state); for (unsigned level = 0; level <= prsrc->base.last_level; ++level) prsrc->plane.layout.slices[level] = @@ -2305,7 +2305,7 @@ panfrost_ptr_unmap(struct pipe_context *pctx, struct pipe_transfer *transfer) struct panfrost_device *dev = pan_device(pctx->screen); if (transfer->usage & PIPE_MAP_WRITE) - prsrc->valid.crc = false; + pan_crc_state_invalidate(&prsrc->crc_state); /* AFBC/AFRC will use a staging resource. `initialized` will be set when * the fragment job is created; this is deferred to prevent useless surface diff --git a/src/gallium/drivers/panfrost/pan_resource.h b/src/gallium/drivers/panfrost/pan_resource.h index 5220b5a11d4..bfd334343c4 100644 --- a/src/gallium/drivers/panfrost/pan_resource.h +++ b/src/gallium/drivers/panfrost/pan_resource.h @@ -76,10 +76,6 @@ struct panfrost_resource { struct panfrost_bo *bo; struct { - /* Is the checksum for this image valid? Implicitly refers to - * the first slice; we only checksum non-mipmapped 2D images */ - bool crc; - /* Has anything been written to this slice? */ BITSET_DECLARE(data, PAN_MAX_MIP_LEVELS); } valid; @@ -102,6 +98,9 @@ struct panfrost_resource { /* Whether the resource owns the backing BO's label */ bool owns_label; + /* CRC state */ + struct pan_crc_state crc_state; + /* AFBC-P state */ struct pan_afbcp *afbcp; }; diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index 3c82d184e97..cd8eaf0bd85 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -1022,7 +1022,7 @@ pan_fb_color_attachment_should_crc(const struct pan_fb_color_attachment *rt, uint64_t mod; struct pan_image_block_size renderblk_sz; - if (!rt->view || rt->discard || !rt->crc_valid || + if (!rt->view || rt->discard || !rt->crc_state || !pan_image_view_has_crc(rt->view)) return false; @@ -1059,7 +1059,7 @@ pan_select_crc_rt(const struct pan_fb_info *fb) continue; /* Select the first RT with a valid CRC buffer. */ - if (*fb->rts[i].crc_valid) { + if (fb->rts[i].crc_state->valid) { best_rt = i; break; } @@ -1083,7 +1083,7 @@ pan_crc_enable(struct pan_crc *crc) * forcefully writing back all the tiles and flush the CRC values. Drawback * is it only works on full frames. */ static void -pan_crc_maybe_enable_flushed(struct pan_crc *crc, +pan_crc_maybe_enable_flushed(struct pan_crc *crc, struct pan_crc_state *state, const struct pan_fb_info *fb) { if (!pan_fb_info_is_fully_covered(fb)) @@ -1091,6 +1091,7 @@ pan_crc_maybe_enable_flushed(struct pan_crc *crc, crc->write = true; crc->force_clean_tile_write = true; + state->valid = true; } static struct pan_crc @@ -1116,18 +1117,17 @@ pan_get_crc_info(const struct pan_fb_info *fb) rt = &fb->rts[crc.index]; /* Transaction Elimination. */ - if (*rt->crc_valid) { + if (rt->crc_state->valid) { pan_crc_enable(&crc); } else { - pan_crc_maybe_enable_flushed(&crc, fb); - *rt->crc_valid = true; + pan_crc_maybe_enable_flushed(&crc, rt->crc_state, fb); } skip: /* Flag CRC buffer states of unselected RTs as invalid. */ for (unsigned i = 0; i < fb->rt_count; i++) - if (i != crc.index && fb->rts[i].crc_valid) - *fb->rts[i].crc_valid = false; + if (i != crc.index && fb->rts[i].crc_state) + fb->rts[i].crc_state->valid = false; return crc; } diff --git a/src/panfrost/lib/pan_desc.h b/src/panfrost/lib/pan_desc.h index 2772d266dc1..4603edbd22d 100644 --- a/src/panfrost/lib/pan_desc.h +++ b/src/panfrost/lib/pan_desc.h @@ -28,7 +28,7 @@ struct pan_compute_dim { struct pan_fb_color_attachment { const struct pan_image_view *view; - bool *crc_valid; + struct pan_crc_state *crc_state; bool clear; bool preload; bool discard; @@ -166,6 +166,11 @@ struct pan_crc { bool force_clean_tile_write : 1; }; +struct pan_crc_state { + /* Is the CRC buffer valid? Implicitly refers to the first slice. */ + bool valid; +}; + struct pan_clean_tile { /* clean_tile_write_enable mask on the 8 color attachments. */ uint8_t write_rt_mask; @@ -189,6 +194,12 @@ pan_crc_is_enabled(struct pan_crc *crc) return crc->index != -1; } +static inline void +pan_crc_state_invalidate(struct pan_crc_state *state) +{ + state->valid = false; +} + static inline bool pan_clean_tile_write_rt_enabled(struct pan_clean_tile clean_tile, unsigned index) From a4bd10c77395ddda8093ea38ec82c43f478c4e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Wed, 11 Feb 2026 13:53:14 +0100 Subject: [PATCH 14/18] pan/crc: Enable CRC for multiple RTs on v6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v6 supports Transaction Elimination with multiple RTs at the condition the write buffer size of the enabled color attachments for a tile doesn't exceed 1600 bytes. Signed-off-by: Loïc Molinari --- src/panfrost/lib/pan_desc.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index cd8eaf0bd85..0c5c0893ac5 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -1048,9 +1048,19 @@ pan_select_crc_rt(const struct pan_fb_info *fb) { int best_rt = -1; -#if PAN_ARCH <= 6 +#if PAN_ARCH <= 5 + /* CRC was introduced in v4 and MRT in v5 but unlike v6 there's no details + * how both work together. */ if (fb->rt_count > 1) return best_rt; + +#elif PAN_ARCH == 6 + /* On v6, all enabled RTs are used to compute a CRC (no crc_render_target + * field on the DBD). The write buffer size of the enabled color + * attachments for a tile must fit within 1600 bytes. */ + if (fb->rt_count > 1 && + pan_cbuf_bytes_per_pixel(fb) * fb->tile_size > 1600) + return best_rt; #endif for (unsigned i = 0; i < fb->rt_count; i++) { From 9249542b11cf6c2075b1724883d242312d8ca1c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Wed, 11 Feb 2026 13:57:18 +0100 Subject: [PATCH 15/18] pan/crc: Disable CRC on v4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't set the CRC buffer pointer nor the row stride on v4. Even if the image props request Transaction Elimination, setting only these fields isn't enough to enable it. The CRC read/write enable fields would need to be appropriately set on the framebuffer descriptor too. Signed-off-by: Loïc Molinari --- src/panfrost/lib/pan_desc.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index 0c5c0893ac5..7f685b675d0 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -1499,11 +1499,6 @@ GENX(pan_emit_fbd)(const struct pan_fb_info *fb, unsigned layer_idx, DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED ? MALI_BLOCK_FORMAT_TILED_U_INTERLEAVED : MALI_BLOCK_FORMAT_LINEAR; - - if (pan_image_view_has_crc(rt)) { - cfg.crc_buffer.row_stride = slayout->crc.stride_B; - cfg.crc_buffer.base = plane->base + slayout->crc.offset_B; - } } if (fb->zs.view.zs) { From 915085886c4626f55b35ad1e94a2c6c10ba93fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Wed, 11 Feb 2026 15:55:15 +0100 Subject: [PATCH 16/18] pan/crc: Enable Empty Tile Elimination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Empty Tile Elimination is an extension to Transaction Elimination that allows to skip the pre-loading of clear tiles that were also clear at the previous render on the selected RT. The crc_clear_color is written as is as CRC value for clear tiles when empty_tile_write_enable is set. If empty_tile_read_enable is set and if a tile is clear at the next render on the selected RT, the written CRC is compared to the crc_clear_color and the processing of the tile is short-circuited if the values are equal. This commit enables Empty Tile Elimination when supported. It also fixes the crc_clear_color value in order to reflect changes of clear values on any of the RTs. This is done by storing a hash of the clear value channels of each cleared RT in the crc_clear_base sub-field. Fixes: 5d5f7552a52 ("panfrost: XML-ify the multi-target framebuffer descriptors") Signed-off-by: Loïc Molinari --- src/panfrost/lib/pan_desc.c | 85 ++++++++++++++++++++++++++++++++++--- src/panfrost/lib/pan_desc.h | 7 +++ 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index 7f685b675d0..96d0830fc3f 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -405,12 +405,7 @@ pan_emit_crc(const struct pan_fb_info *fb, struct pan_crc *crc, #if PAN_ARCH >= 7 cfg->render_target = crc->index; - - if (fb->rts[crc->index].clear) { - uint32_t clear_val = fb->rts[crc->index].clear_value[0]; - cfg->clear_color = clear_val | 0xc000000000000000 | - (((uint64_t)clear_val & 0xffff) << 32); - } + cfg->clear_color = crc->clear_color; #endif } @@ -1104,6 +1099,69 @@ pan_crc_maybe_enable_flushed(struct pan_crc *crc, struct pan_crc_state *state, state->valid = true; } +#if PAN_ARCH >= 7 +static uint64_t +pan_crc_clear_color(const struct pan_fb_info *fb) +{ + uint64_t crc_clear_flag = 1; + uint64_t crc_clear_base = 0; + uint64_t crc_init = 0; + + /* When a tile is clear (i.e. no polygons intersect it), the configured + * crc_clear_color is written as is as CRC value by the GPU if both CRC + * write (crc_write_enable flag) and Empty Tile Elimination write + * (empty_tile_write_enable flag) are enabled. If Empty Tile Elimination + * read (empty_tile_read_enable flag) is enabled, this then allows to skip + * the pre-loading of clear tiles which were also clear at the previous + * render on the selected RT. It's done by comparing CRCs in the CRC buffer + * to the crc_clear_color. + * + * The crc_clear_flag sub-field (bit 63) is flagged set here. It's flipped + * by the GPU when writing standard (i.e. non-empty) CRCs. + * + * v10 introduced the crc_init sub-field (bits 15:0). v7 and v9 can use + * those as additional crc_clear_base bits. We don't use it for now and + * keep those 16 bits clear regardless of arch. + * + * This leaves 47 bits in the crc_clear_base sub-field (bits 62:16). Clear + * color changes on any RTs must be reflected into this field in order to + * properly invalidate CRCs stored this way. This is done by hashing the + * clear value channels of each cleared RT. Each clear color channel value + * is multiplied with a prime number followed by a XOR to the destination + * hash. Clear values in pan_fb_info struct are expected to be packed with + * respect to the format and dithering of the underlying RTs so that a + * change of format (without a clear color change) can generate a different + * hash. The prime number 32749 is carefully selected so that the 32 bits + * of each clear color channel take at most 47 bits after the mul (the next + * prime number 32771 takes at most 48 bits). The resulting hash value is + * guaranteed not to overflow and can safely be packed. */ + + for (unsigned i = 0; i < fb->rt_count; ++i) + if (fb->rts[i].clear) + for (unsigned j = 0; j < 4; ++j) + crc_clear_base ^= 32749 * fb->rts[i].clear_value[j]; + + return (crc_clear_flag << 63) | (crc_clear_base << 16) | crc_init; +} +#endif + +#if PAN_ARCH >= 6 +static bool +pan_crc_has_empty_tile_elimination(struct pan_crc *crc, + const struct pan_fb_info *fb) +{ +#if PAN_ARCH == 6 + /* For v6, there's no details how MRT interacts with Empty Tile + * Elimination, especially how the clear value is generated from the color + * attachment clear values. The feature is disabled for that use case. */ + if (fb->rt_count > 1) + return false; +#endif + + return crc->read || crc->write; +} +#endif + static struct pan_crc pan_get_crc_info(const struct pan_fb_info *fb) { @@ -1133,6 +1191,17 @@ pan_get_crc_info(const struct pan_fb_info *fb) pan_crc_maybe_enable_flushed(&crc, rt->crc_state, fb); } +#if PAN_ARCH >= 6 + /* Empty Tile Elimination. */ + if (pan_crc_has_empty_tile_elimination(&crc, fb)) { +#if PAN_ARCH >= 7 + crc.clear_color = pan_crc_clear_color(fb); +#endif + crc.empty_tile_read = crc.read; + crc.empty_tile_write = crc.write; + } +#endif + skip: /* Flag CRC buffer states of unselected RTs as invalid. */ for (unsigned i = 0; i < fb->rt_count; i++) @@ -1322,6 +1391,10 @@ GENX(pan_emit_fbd)(const struct pan_fb_info *fb, unsigned layer_idx, if (pan_crc_is_enabled(&crc)) { cfg.crc_read_enable = crc.read; cfg.crc_write_enable = crc.write; +#if PAN_ARCH >= 7 + cfg.empty_tile_read_enable = crc.empty_tile_read; + cfg.empty_tile_write_enable = crc.empty_tile_write; +#endif } #if PAN_ARCH >= 9 diff --git a/src/panfrost/lib/pan_desc.h b/src/panfrost/lib/pan_desc.h index 4603edbd22d..50c4b5041c5 100644 --- a/src/panfrost/lib/pan_desc.h +++ b/src/panfrost/lib/pan_desc.h @@ -155,6 +155,9 @@ struct pan_fb_info { }; struct pan_crc { + /* Empty Tile Elimination clear color */ + uint64_t clear_color; + /* Selected RT index (8 max), -1 if none. */ int8_t index; @@ -164,6 +167,10 @@ struct pan_crc { /* Force clean writes for CRC buffer init */ bool force_clean_tile_write : 1; + + /* Empty Tile Elimination flags */ + bool empty_tile_read : 1; + bool empty_tile_write : 1; }; struct pan_crc_state { From 74c26ebaa4bd515386cb81579625530b7c455e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Wed, 11 Feb 2026 16:07:54 +0100 Subject: [PATCH 17/18] pan/crc: Optimize clear color hashing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Take advantage of XOR associativity to break the loop-carried dependency chain and help compiler auto-vectorization. Signed-off-by: Loïc Molinari --- src/panfrost/lib/pan_desc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index 96d0830fc3f..b074c616839 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -1103,6 +1103,7 @@ pan_crc_maybe_enable_flushed(struct pan_crc *crc, struct pan_crc_state *state, static uint64_t pan_crc_clear_color(const struct pan_fb_info *fb) { + uint64_t base[4] = { 0, }; /* Compiler auto-vectorization hint */ uint64_t crc_clear_flag = 1; uint64_t crc_clear_base = 0; uint64_t crc_init = 0; @@ -1139,7 +1140,9 @@ pan_crc_clear_color(const struct pan_fb_info *fb) for (unsigned i = 0; i < fb->rt_count; ++i) if (fb->rts[i].clear) for (unsigned j = 0; j < 4; ++j) - crc_clear_base ^= 32749 * fb->rts[i].clear_value[j]; + base[i] ^= 32749 * fb->rts[i].clear_value[j]; + + crc_clear_base = (base[0] ^ base[1]) ^ (base[2] ^ base[3]); return (crc_clear_flag << 63) | (crc_clear_base << 16) | crc_init; } From 133959027329a467140439f5591e4c9bbbb48f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Thu, 12 Feb 2026 08:52:41 +0100 Subject: [PATCH 18/18] pan/crc: Zero-filled CRC buffer invalidation for v7+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set the crc_clear_color field in a specific way ensuring that the all-zero CRC value can't ever be generated by the GPU. For v7+, this allows to invalidate a CRC buffer by zero-filling it instead of having to wait for a full frame draw. The drawback is that the CRC buffer must be mapped on the CPU to do so. The CRC state is extended with a pan_ptr that must be appropriately set by the lib user prior to a pan_emit_fbd() call so that it can access the CRC buffer memory. Signed-off-by: Loïc Molinari --- src/gallium/drivers/panfrost/pan_resource.c | 7 +++ src/panfrost/lib/pan_desc.c | 53 +++++++++++++++++---- src/panfrost/lib/pan_desc.h | 9 ++++ 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/gallium/drivers/panfrost/pan_resource.c b/src/gallium/drivers/panfrost/pan_resource.c index 7a004320a0e..5fa8fa62029 100644 --- a/src/gallium/drivers/panfrost/pan_resource.c +++ b/src/gallium/drivers/panfrost/pan_resource.c @@ -264,6 +264,8 @@ panfrost_resource_import_bo(struct panfrost_resource *rsc, if (!rsc->bo) return -1; + pan_crc_state_set_ptr(&rsc->crc_state, &rsc->bo->ptr); + return 0; } @@ -1150,6 +1152,7 @@ panfrost_resource_create_with_modifier(struct pipe_screen *screen, so->bo = panfrost_bo_create(dev, so->plane.layout.data_size_B, flags, res_label); + pan_crc_state_set_ptr(&so->crc_state, &so->bo->ptr); if (!so->bo) { panfrost_resource_destroy(screen, &so->base); @@ -1698,6 +1701,7 @@ panfrost_ptr_map(struct pipe_context *pctx, struct pipe_resource *resource, panfrost_bo_unreference(rsrc->bo); rsrc->bo = newbo; rsrc->plane.base = newbo->ptr.gpu; + pan_crc_state_set_ptr(&rsrc->crc_state, &newbo->ptr); if (!copy_resource && drm_is_afbc(rsrc->modifier)) { if (panfrost_resource_init_afbc_headers(rsrc)) @@ -1871,6 +1875,7 @@ pan_resource_modifier_convert(struct panfrost_context *ctx, rsrc->bo = tmp_rsrc->bo; rsrc->plane.base = rsrc->bo->ptr.gpu; panfrost_bo_reference(rsrc->bo); + pan_crc_state_set_ptr(&rsrc->crc_state, &rsrc->bo->ptr); rsrc->owns_label = tmp_rsrc->owns_label; tmp_rsrc->owns_label = false; @@ -2218,6 +2223,7 @@ pan_resource_afbcp_commit(struct panfrost_context *ctx, panfrost_bo_unreference(prsrc->bo); prsrc->bo = prsrc->afbcp->packed_bo; prsrc->afbcp->packed_bo = NULL; + pan_crc_state_set_ptr(&prsrc->crc_state, &prsrc->bo->ptr); pan_resource_afbcp_stop(prsrc); } @@ -2329,6 +2335,7 @@ panfrost_ptr_unmap(struct pipe_context *pctx, struct pipe_transfer *transfer) prsrc->bo = pan_resource(trans->staging.rsrc)->bo; prsrc->plane.base = prsrc->bo->ptr.gpu; panfrost_bo_reference(prsrc->bo); + pan_crc_state_set_ptr(&prsrc->crc_state, &prsrc->bo->ptr); prsrc->owns_label = pan_resource(trans->staging.rsrc)->owns_label; pan_resource(trans->staging.rsrc)->owns_label = false; diff --git a/src/panfrost/lib/pan_desc.c b/src/panfrost/lib/pan_desc.c index b074c616839..78d0e9b2687 100644 --- a/src/panfrost/lib/pan_desc.c +++ b/src/panfrost/lib/pan_desc.c @@ -1084,6 +1084,28 @@ pan_crc_enable(struct pan_crc *crc) crc->write = true; } +#if PAN_ARCH >= 7 +/* Initialize the CRC buffer by zero'ing it. The all-zero CRC can't collide + * thanks to the crc_clear_color field, see pan_crc_clear_color(). Drawback is + * the CRC BO must be CPU mapped. */ +static void +pan_crc_enable_zeroed(struct pan_crc *crc, struct pan_crc_state *state, + const struct pan_image_view *view) +{ + const struct pan_image_plane_ref pref = + pan_image_view_get_color_plane(view); + const struct pan_image_plane *plane = pref.image->planes[pref.plane_idx]; + const struct pan_image_slice_layout *slice = + &plane->layout.slices[view->first_level]; + + assert(state->ptr && state->ptr->cpu); + memset(state->ptr->cpu + slice->crc.offset_B, 0, slice->crc.size_B); + + pan_crc_enable(crc); + state->valid = true; +} +#endif + /* Take advantage of a full frame draw to initialize the CRC buffer by * forcefully writing back all the tiles and flush the CRC values. Drawback * is it only works on full frames. */ @@ -1104,8 +1126,8 @@ static uint64_t pan_crc_clear_color(const struct pan_fb_info *fb) { uint64_t base[4] = { 0, }; /* Compiler auto-vectorization hint */ - uint64_t crc_clear_flag = 1; - uint64_t crc_clear_base = 0; + uint64_t crc_clear_flag = 0; + uint64_t crc_clear_base = 1ull << 46; uint64_t crc_init = 0; /* When a tile is clear (i.e. no polygons intersect it), the configured @@ -1117,14 +1139,18 @@ pan_crc_clear_color(const struct pan_fb_info *fb) * render on the selected RT. It's done by comparing CRCs in the CRC buffer * to the crc_clear_color. * - * The crc_clear_flag sub-field (bit 63) is flagged set here. It's flipped - * by the GPU when writing standard (i.e. non-empty) CRCs. + * The crc_clear_flag sub-field (bit 63) is flagged unset here. It's + * flipped by the GPU when writing standard (i.e. non-empty) CRCs. This + * prevents standard CRCs from using the all-zero CRC value. Empty CRCs + * can't use the all-zero CRC value either because crc_clear_base's most + * significant bit is flagged set here. This allows to invalidate a CRC + * buffer by zero'ing it. * * v10 introduced the crc_init sub-field (bits 15:0). v7 and v9 can use * those as additional crc_clear_base bits. We don't use it for now and * keep those 16 bits clear regardless of arch. * - * This leaves 47 bits in the crc_clear_base sub-field (bits 62:16). Clear + * This leaves 46 bits in the crc_clear_base sub-field (bits 62:16). Clear * color changes on any RTs must be reflected into this field in order to * properly invalidate CRCs stored this way. This is done by hashing the * clear value channels of each cleared RT. Each clear color channel value @@ -1132,17 +1158,17 @@ pan_crc_clear_color(const struct pan_fb_info *fb) * hash. Clear values in pan_fb_info struct are expected to be packed with * respect to the format and dithering of the underlying RTs so that a * change of format (without a clear color change) can generate a different - * hash. The prime number 32749 is carefully selected so that the 32 bits - * of each clear color channel take at most 47 bits after the mul (the next - * prime number 32771 takes at most 48 bits). The resulting hash value is + * hash. The prime number 16381 is carefully selected so that the 32 bits + * of each clear color channel take at most 46 bits after the mul (the next + * prime number 16411 takes at most 47 bits). The resulting hash value is * guaranteed not to overflow and can safely be packed. */ for (unsigned i = 0; i < fb->rt_count; ++i) if (fb->rts[i].clear) for (unsigned j = 0; j < 4; ++j) - base[i] ^= 32749 * fb->rts[i].clear_value[j]; + base[i] ^= 16381 * fb->rts[i].clear_value[j]; - crc_clear_base = (base[0] ^ base[1]) ^ (base[2] ^ base[3]); + crc_clear_base |= (base[0] ^ base[1]) ^ (base[2] ^ base[3]); return (crc_clear_flag << 63) | (crc_clear_base << 16) | crc_init; } @@ -1191,7 +1217,14 @@ pan_get_crc_info(const struct pan_fb_info *fb) if (rt->crc_state->valid) { pan_crc_enable(&crc); } else { +#if PAN_ARCH >= 7 + if (rt->crc_state->ptr && rt->crc_state->ptr->cpu) + pan_crc_enable_zeroed(&crc, rt->crc_state, rt->view); + else + pan_crc_maybe_enable_flushed(&crc, rt->crc_state, fb); +#else pan_crc_maybe_enable_flushed(&crc, rt->crc_state, fb); +#endif } #if PAN_ARCH >= 6 diff --git a/src/panfrost/lib/pan_desc.h b/src/panfrost/lib/pan_desc.h index 50c4b5041c5..3f2ec6a6b85 100644 --- a/src/panfrost/lib/pan_desc.h +++ b/src/panfrost/lib/pan_desc.h @@ -174,6 +174,9 @@ struct pan_crc { }; struct pan_crc_state { + /* Pointer to BO mapping. */ + struct pan_ptr *ptr; + /* Is the CRC buffer valid? Implicitly refers to the first slice. */ bool valid; }; @@ -207,6 +210,12 @@ pan_crc_state_invalidate(struct pan_crc_state *state) state->valid = false; } +static inline void +pan_crc_state_set_ptr(struct pan_crc_state *state, struct pan_ptr *ptr) +{ + state->ptr = ptr; +} + static inline bool pan_clean_tile_write_rt_enabled(struct pan_clean_tile clean_tile, unsigned index)