pan/crc: Enable Empty Tile Elimination

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: 5d5f7552a5 ("panfrost: XML-ify the multi-target framebuffer descriptors")
Signed-off-by: Loïc Molinari <loic.molinari@collabora.com>
This commit is contained in:
Loïc Molinari 2026-02-11 15:55:15 +01:00
parent 9249542b11
commit 915085886c
2 changed files with 86 additions and 6 deletions

View file

@ -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

View file

@ -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 {