Skip to content

Samples-passed reports count from the first one when the game never resets the counter (Red Dead Redemption's missing character bodies) - #1867

Closed
robandstuff wants to merge 1 commit into
seedds:mainfrom
robandstuff:fix/occlusion-counter-without-reset
Closed

robandstuff wants to merge 1 commit into
seedds:mainfrom
robandstuff:fix/occlusion-counter-without-reset

Conversation

@robandstuff

Copy link
Copy Markdown
Contributor

What and why

In Red Dead Redemption, characters more than a few metres away were drawn as their hats and props only. Their bodies were missing from the main view, although their shadows had them. The game's character occlusion test (rage::CharacterVisQueries::TestVisiblity) takes end − begin of two SAMPLES_PASSED reports around each character's box. A character at or below its threshold goes on an occluded list, and renDrawBucketContainer::AddToDrawList then drops its low-detail body.

The game never calls ResetCounter: about 35 queries and 324 reports a frame, no resets. On the GPU the counter runs from the queue's creation. The host only started counting at a reset, so every report read 0 and every character counted as hidden.

Now a samples-passed report with no reset before it starts counting. If the open encoder has no visibility buffer, the pass is split there. From then on each report holds the samples since the previous report, added to a running total when its submission completes. post_run does that in submission order under g_post_mu. A reset returns to counting from the reset, as before. DM_NVN_OCC_IMPLICIT=0 turns it off.

Reference entry: docs/reference/rendering.md, "Samples-passed reports with no reset before them". The game's profile isn't upstream yet. It is carried in LNPANDY/NX2Apple and will come in its own PR (tracked in LNPANDY/NX2Apple#999).

Checked

  • Tests: make -C dm/host/tests GAME=0100000000010000 run: 49 tests, 2,355 checks, none failed. make -C dm/gfx/tests GAME=0100000000010000 run on Apple silicon (Metal): 146 tests, 71,870 checks. The failures are t_render_counter_overlap (1) and t_render_enable_queue (9), which fail the same way on unchanged main (7f50222); t_feedback's check "two reports in one command buffer read two times" (timestamp reports) fails now and then on unchanged main too (2 of 6 runs), and on this branch in 2 of 3 runs. This change only touches samples-passed reports.
  • Games run, with title ID and version: Red Dead Redemption 01007820196A6000 1.0.4, on the Mac from a save in Armadillo:
    • A recorded input replay: the distant characters have bodies at frames 2,280–2,340 (before the change, a floating hat for Jake); frame time unchanged at 33.3 ms; no [RED].
    • Played by hand with the fix: distant characters drawn whole.
  • Devices and OS: Apple M4 (32 GB), macOS 27.0.1. Not tested on an iPhone or iPad.
  • scripts/regress.py check --games was not run: no other game's saves or recordings were at hand. Games that call ResetCounter before their first report take the old path unchanged.

Docs

  • The finding is in its docs/reference/rendering.md entry, and python3 dm/tools/docs_symptoms.py was run (the symptom index)
  • No chain tool changed
  • No game files, keys or data extracted from a game in the change or the attachments

🤖 Generated with Claude Code

…assed reports count from the first one when the game never resets the counter

The game's character occlusion queries (rage::CharacterVisQueries::TestVisiblity) take end - begin of two SAMPLES_PASSED reports around each character's box and never call ResetCounter: on the GPU the counter runs from the queue's creation. Counting started only at a reset, so every report was 0, every character counted as hidden, and the main view dropped the low-detail bodies of characters a few metres away (hats and props still drew; shadows kept the bodies).

With no reset before it, the first samples-passed report now starts counting; each report holds the samples since the previous one, added to a running total when it completes (records complete in submission order, under g_post_mu). A reset returns to the old mode. DM_NVN_OCC_IMPLICIT=0 turns it off.

Verified on 1.0.4 (01007820196A6000) in a recorded replay and played on the Mac: distant NPCs have bodies; frame time unchanged; no [RED].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) docs Documentation labels Nov 8, 2026
seedds added a commit that referenced this pull request Nov 9, 2026
…e that never resets it reads its occlusion queries again (Red Dead Redemption's distant characters had no bodies), and a report a submission after its reset no longer reads slots a later submission overwrote (PR #1867)

On the GPU the counter runs from the queue's creation and ResetCounter zeroes it. The host
counted only from a reset, so a game that never resets read 0 from every report: Red Dead
Redemption's character occlusion queries (end - begin of two reports around each box) counted
every character as hidden and dropped the bodies of those a few metres away.

Each report also re-read every visibility slot since the reset, including slots of earlier
submissions whose buffer the ring had already handed to a later one: a reset and a draw in one
submission and a half-sized draw and the report in the next read 64 samples instead of 96.

Now post_run keeps one running total in submission order under g_post_mu. A report adds the
slots since the previous report and writes the total; the slots left at a submission's end are
added at its completion, so each slot is read once while its buffer is still that
submission's; a reset zeroes the total at its place in the queue. Counting starts at the first
reset or, with none before it, at the first report.

Checked: t_render_enable_queue's two new checks (end - begin with no reset across submissions;
a reset with its report one submission later) fail on main and pass; the whole gfx run list
passes (149 groups); test_docs. The contributor's Red Dead Redemption 1.0.4 replay and play on
an M4 were on the PR's first version, which gives that game the same values. No regression
run: no local game has libraries for chain dedc9391c30b, and translating one is far over the
five minutes a game. Uncertain reach (every occlusion-query game's path).

Co-authored-by: Robert McFadden <20628004+robandstuff@users.noreply.github.com>
@seedds

seedds commented Nov 9, 2026

Copy link
Copy Markdown
Owner

Thank you, landed as cfbdd93 with you as co-author: your diagnosis, reference entry and symptom line are kept.

The code was reworked into one model of the counter instead of a second mode with a switch: post_run keeps a single running total in submission order. Each report adds the slots since the previous report, the slots left at a submission's end are added when it completes, and a reset zeroes the total at its place in the queue. Counting starts at the first reset or, with none before it, at the first report. A game that never resets gets the same values as your version, and DM_NVN_OCC_IMPLICIT is gone.

The rework also fixes a second bug in the old reset path: a report one submission after its reset re-read slots whose visibility buffer a later submission had already reused (64 samples instead of 96 in the new t_render_enable_queue check). The whole gfx run list passes here (149 groups).

Your Red Dead Redemption check was on your first version. A run of the landed commit on your side would confirm it, as would the profile PR you mentioned (LNPANDY/NX2Apple#999) when it comes.

@seedds seedds closed this Nov 9, 2026
robandstuff added a commit to robandstuff/NX2Apple that referenced this pull request Nov 9, 2026
…d as cfbdd93c

seedds took the occlusion fix (seedds/nxnative#1867) as cfbdd93c, reworked into one running total in queue order with no switch: DM_NVN_OCC_IMPLICIT is gone, and a report one submission after its reset no longer reads a reused buffer. 0104 is now that commit as a backport (without the generated symptom index), so the next bump drops it; 0105, the note, follows it.

Checked on Red Dead Redemption 1.0.4 with a host built from this series: the recorded replay from Armadillo reaches frame 3,600 with no [RED], frame time p50 33.3 ms, and at the saloon (frame 2,310) Jake and the people down the street have bodies. python3 tools/engine.py check: 71 patches apply cleanly to c78d737.

Issue: LNPANDY#999

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@robandstuff

Copy link
Copy Markdown
Contributor Author

Thanks for taking it. Confirmed on Red Dead Redemption 1.0.4 with cfbdd93, on a Mac host built from NX2Apple's pin (c78d737) with the landed commit applied in place of my first version (Apple M4, macOS 27.0.1):

  • The log has occlusion queries: samples passed reported with no ResetCounter before it: counting from this report once.
  • The recorded replay from Armadillo: at the saloon (frame 2,310) Jake and the people down the street have their bodies again, as with the first version.
  • No [RED] through frame 3,600; frame time p50 33.3 ms, as before.

NX2Apple now carries cfbdd93 as a backport in place of its own patch (LNPANDY/NX2Apple#1000), and the profile follows in LNPANDY/NX2Apple#999.

robandstuff added a commit to robandstuff/NX2Apple that referenced this pull request Nov 9, 2026
…ing fixes found on it, and a test for rpf6.py

Red Dead Redemption on the current pin (c78d737). The movies' second audio renderer plays through upstream's support for several renderers, so the fork carries no audio patch for it.

0102: the shader split reads Rockstar RPF6 archives, RAGE rgxd effects and Double Eleven .pkdswitch packs.
0103-0105: the profile and note, 1.0.4's archive key (read from its key setup), and the registration of 1.0.4.
0106: a draw that samples the depth attachment its render pass already wrote ends the pass first: the cloud fog read partly stored tiles and left flickering blocks in the sky over distant hills. DM_NVN_FB_DEPTH=0 turns it off. Forwarded as seedds/nxnative#1866.
0107: upstream's fix for the samples-passed counter (cfbdd93c, from seedds/nxnative#1867) as a backport: the game never resets the counter, and its distant characters were drawn as hats and props only.
0108: the game note after the two fixes.
0109: test_rpf6, synthetic bytes for the archive header, the entry table and key_from_setup.
0102-0105, 0108 and 0109 are forwarded as seedds/nxnative#2005.

Checked: python3 tools/engine.py check (75 patches apply to c78d737). The gfx suite on this pin with 0102-0108: 136 groups, 71,647 checks, 10 failed in t_present_crop, t_render_counter_overlap and t_render_enable_queue; the same 10 fail without 0107, which adds 6 passing checks. Red Dead Redemption 1.0.4 built from the dump and replayed from a save in Armadillo: no [RED], frame time p50 33.3 ms, the distant characters have bodies and the sky is clean.

Issue: LNPANDY#999
Know-It-All-Brother pushed a commit to Know-It-All-Brother/nxnative that referenced this pull request Nov 9, 2026
…e that never resets it reads its occlusion queries again (Red Dead Redemption's distant characters had no bodies), and a report a submission after its reset no longer reads slots a later submission overwrote (PR seedds#1867)

On the GPU the counter runs from the queue's creation and ResetCounter zeroes it. The host
counted only from a reset, so a game that never resets read 0 from every report: Red Dead
Redemption's character occlusion queries (end - begin of two reports around each box) counted
every character as hidden and dropped the bodies of those a few metres away.

Each report also re-read every visibility slot since the reset, including slots of earlier
submissions whose buffer the ring had already handed to a later one: a reset and a draw in one
submission and a half-sized draw and the report in the next read 64 samples instead of 96.

Now post_run keeps one running total in submission order under g_post_mu. A report adds the
slots since the previous report and writes the total; the slots left at a submission's end are
added at its completion, so each slot is read once while its buffer is still that
submission's; a reset zeroes the total at its place in the queue. Counting starts at the first
reset or, with none before it, at the first report.

Checked: t_render_enable_queue's two new checks (end - begin with no reset across submissions;
a reset with its report one submission later) fail on main and pass; the whole gfx run list
passes (149 groups); test_docs. The contributor's Red Dead Redemption 1.0.4 replay and play on
an M4 were on the PR's first version, which gives that game the same values. No regression
run: no local game has libraries for chain dedc9391c30b, and translating one is far over the
five minutes a game. Uncertain reach (every occlusion-query game's path).

Co-authored-by: Robert McFadden <20628004+robandstuff@users.noreply.github.com>
@robandstuff
robandstuff deleted the fix/occlusion-counter-without-reset branch November 12, 2026 05:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants