up_begin hands the encode thread what is recorded before it waits for a busy upload slot: the slot's last batch could still be an uncommitted record

With the encode thread on (the default), rec_commit_upload only appends a commit record to the encode thread's current block, which is handed over when it fills or the submission drains. up_begin waited for the next of the three slots to finish its last batch, which could be one of those records: never committed, never finished. Little Nightmares II's first render passes zero-filled enough new targets (tex_zero_fill) to come round to a slot within one block; its RHIThread waited in up_wait_zero inside nvnQueueSubmitCommands for good, the render thread on its lock, and after 10 s Unreal's garbage collector gave up on the render fences (the backtrace with host frames: up_wait_zero ← up_begin ← tex_zero_fill ← tex_mtl ← attach ← rp_begin_ex ← lc_land ← mtl_exec ← h_nvnQueueSubmitCommands). q_cb already drains before it waits for the same reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
arielherselfandClaude Opus 5.5 authored and seedds committed 2026-10-24 09:12:22 +08:00
1 parent 6de59702ec
commit 9cb7af3237
1 file changed
+4
+4
View File
@@ -1581,6 +1581,10 @@ static void up_begin(void) {
up_owner_check("up_begin");
if (g_up_cb) return;
unsigned slot = g_up_seq++ % 3;
// The slot's last batch may still be only a commit record in the encode thread's current block (rec_commit_upload), which is handed over when it fills or the submission drains:
// waiting without handing it over waited for a batch never committed. Little Nightmares II's first render passes zero-filled enough new targets to come round to a slot within one
// block, and its RHI thread waited for good inside nvnQueueSubmitCommands (q_cb drains before it waits for the same reason)
if (atomic_load(&g_up_alloc_busy[slot])) rec_publish();
up_wait_zero(&g_up_alloc_busy[slot]);
atomic_store(&g_up_alloc_busy[slot], 1); g_up_cur_slot = slot;
[g_up_alloc4[slot] reset];