r300: split large HiZ clears into multiple packets

R300_PACKET3_3D_CLEAR_HIZ encodes COUNT in 14 bits (COUNT[13:0]), so a
single packet can clear at most 0x3fff dwords.

Large depth surfaces on R5xx can require more HiZ dwords than that.
When we emitted a single packet, COUNT truncated and part of HiZ RAM
remained uncleared, which could show up as HyperZ corruption.

Emit CLEAR_HIZ in chunks of R300_CLEAR_HIZ_COUNT_MAX and reserve enough
atom space for the worst-case packet count derived.

Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/360
Fixes: 12dcbd5954 ("r300g: enable Hyper-Z by default on r500")
(cherry picked from commit fddc101070)

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40752>
This commit is contained in:
Pavel Ondračka 2026-02-23 14:12:36 +01:00 committed by Eric Engestrom
parent 2b87cb01b2
commit dfd0e55b5a
4 changed files with 37 additions and 11 deletions

View file

@ -654,7 +654,7 @@
"description": "r300: split large HiZ clears into multiple packets",
"nominated": true,
"nomination_type": 2,
"resolution": 0,
"resolution": 1,
"main_sha": null,
"because_sha": "12dcbd5954676ee32604d82cacbf9a4259967e13",
"notes": null

View file

@ -146,6 +146,19 @@ do { \
return false; \
} while (0)
static unsigned r300_hiz_clear_atom_size(const struct r300_screen *screen)
{
if (screen->caps.hiz_ram <= 0)
return 0;
unsigned pipes = r300_hyperz_pipe_count(screen);
unsigned max_hiz_dwords = screen->caps.hiz_ram * pipes;
unsigned max_hiz_packets =
(max_hiz_dwords + R300_CLEAR_HIZ_COUNT_MAX - 1) /
R300_CLEAR_HIZ_COUNT_MAX;
return max_hiz_packets * 4;
}
static bool r300_setup_atoms(struct r300_context* r300)
{
bool is_rv350 = r300->screen->caps.is_rv350;
@ -203,8 +216,10 @@ static bool r300_setup_atoms(struct r300_context* r300)
/* TX. */
R300_INIT_ATOM(texture_cache_inval, 2);
R300_INIT_ATOM(textures_state, 0);
/* Clear commands */
R300_INIT_ATOM(hiz_clear, r300->screen->caps.hiz_ram > 0 ? 4 : 0);
/* Clear commands.
* 3D_CLEAR_HIZ uses COUNT[13:0], so large clears are split into chunks.
* Reserve enough dwords for the worst-case per-chip HiZ RAM size. */
R300_INIT_ATOM(hiz_clear, r300_hiz_clear_atom_size(r300->screen));
R300_INIT_ATOM(zmask_clear, r300->screen->caps.zmask_ram > 0 ? 4 : 0);
R300_INIT_ATOM(cmask_clear, 4);
/* ZB (unpipelined), SU. */

View file

@ -1244,17 +1244,26 @@ void r300_emit_hiz_clear(struct r300_context *r300, unsigned size, void *state)
{
struct pipe_framebuffer_state *fb =
(struct pipe_framebuffer_state*)r300->fb_state.state;
struct r300_resource* tex;
struct r300_resource *tex = r300_resource(fb->zsbuf.texture);
unsigned remaining = tex->tex.hiz_dwords[fb->zsbuf.level];
unsigned start = 0;
CS_LOCALS(r300);
tex = r300_resource(fb->zsbuf.texture);
/* 3D_CLEAR_HIZ COUNT is 14-bit (max 0x3fff), so large surfaces must be
* split into multiple packets. */
while (remaining) {
unsigned count = MIN2(remaining, R300_CLEAR_HIZ_COUNT_MAX);
BEGIN_CS(size);
OUT_CS_PKT3(R300_PACKET3_3D_CLEAR_HIZ, 2);
OUT_CS(0);
OUT_CS(tex->tex.hiz_dwords[fb->zsbuf.level]);
OUT_CS(r300->hiz_clear_value);
END_CS;
BEGIN_CS(4);
OUT_CS_PKT3(R300_PACKET3_3D_CLEAR_HIZ, 2);
OUT_CS(start);
OUT_CS(count);
OUT_CS(r300->hiz_clear_value);
END_CS;
start += count;
remaining -= count;
}
/* Mark the current zbuffer's hiz ram as in use. */
r300->hiz_in_use = true;

View file

@ -3485,6 +3485,8 @@ enum {
* 2. CLEAR_VALUE: Value to write into HIZ RAM.
*/
#define R300_PACKET3_3D_CLEAR_HIZ 0x00003700
/* 3D_CLEAR_HIZ COUNT field width (COUNT[13:0]). */
#define R300_CLEAR_HIZ_COUNT_MAX 0x3fff
#define R300_PACKET3_3D_CLEAR_CMASK 0x00003800
/* Draws a set of primitives using vertex buffers pointed by the state data.