Set vk_sub_in_frame under the submission lock from the place the submitting thread noted, so a thread about to submit no longer changes it under another thread's replay.

vk_sub_in_frame says which of its frame's submissions the one being recorded is; vk_memory.c sub_place files the mirror
pre-snapshot's statistics by it during the replay, on the replaying thread and its workers. vk_be_submit wrote it on
every submitting thread before submit_sync, outside sub_lock, so with the replay on the submitting threads another
thread entering a submit changed it under a replay in progress, and the replay filed its work at the other
submission's place. Each thread now notes the place in a thread-local (vk_be_submit, and replay_main for the
asynchronous replay), and vk_sync.c submit publishes it right after taking the lock for a submission with commands
(vk_sub_in_frame_publish, a weak no-op without the back end). Submissions without commands of their own (held work, the
present thread's blit) leave it as it was, as before. Performance heuristics only; no timing change.

Not reproduced before the change. Checks that passed: pass_cc_check on every runtime file (a header changed), no new
-Wall -Wextra warnings in vk_backend.c and vk_sync.c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-13 19:12:51 -05:00
parent 0e918d4846
commit 518185b808
3 changed files with 16 additions and 3 deletions

View File

@@ -963,6 +963,9 @@ void vk_gpu_parts_log(uint64_t frame, uint64_t t0, uint64_t t1, double fence_ms)
void vk_gtl_note(int kind, uint64_t sub_ns, uint64_t start_tick, uint64_t end_tick);
void vk_gtl_log(uint64_t frame, int frames);
extern int vk_sub_in_frame; // replay: the submission being recorded is the frame's n-th (0 the first; vk_backend.c)
// vk_sync.c submit, sub_lock held, for a submission with commands: vk_sub_in_frame takes the place in its frame the
// submitting thread noted for it (vk_backend.c vk_be_submit, replay_main).
void vk_sub_in_frame_publish(void);
// Runs fn(arg) on the completion thread once fence is done on the GPU (event writes, counter reports, sync state).
// Callbacks run in the order they were added, after those of earlier values; a value already done runs at once there.
void vk_completion_add(uint64_t fence, void (*fn)(void *), void *arg);

View File

@@ -634,7 +634,14 @@ static uint64_t submit_sync(const uint64_t *handles, int n) {
return f;
}
// vk_sub_in_frame is the replay's: vk_sync.c submit sets it under the submission lock from the place the submitting
// thread noted for its submission (vk_sub_in_frame_publish), so another thread about to submit cannot change it under
// a replay in progress (vk_memory.c sub_place reads it there and on the replay workers). Submissions without commands
// of their own (held work, the present thread's blit) leave it as it was.
int vk_sub_in_frame;
static _Thread_local int sub_place_next; // this thread's next submission's place in its frame
void vk_sub_in_frame_publish(void) { vk_sub_in_frame = sub_place_next; }
static void replay_main(void *arg) {
(void)arg;
@@ -650,7 +657,7 @@ static void replay_main(void *arg) {
plat_cond_broadcast(&rcv);
plat_mutex_unlock(&rl);
if (it.kind == RI_SUBMIT) {
vk_sub_in_frame = sub_idx++;
sub_place_next = sub_idx++;
uint64_t f = submit_sync(it.h, it.n);
if (f != it.value)
rt_fatal("vk: replayed submission signalled %llu, queued as %llu", (unsigned long long)f,
@@ -933,7 +940,7 @@ static void vk_be_program_release(const prog_t *p) { replay_drain(DR_PROG_RELEAS
static uint64_t vk_be_submit(const uint64_t *handles, int n) {
vk_lowlat_submit_start(); // a frame's first submission: its simulation is over (nothing while low latency is off)
if (!gfx_replay_async()) {
vk_sub_in_frame = __atomic_load_n(&nvn_submits_in_frame, __ATOMIC_RELAXED); // the front end counts it up after the submit returns
sub_place_next = __atomic_load_n(&nvn_submits_in_frame, __ATOMIC_RELAXED); // counted up after the submit returns
return submit_sync(handles, n);
}
vk_sync_start();

View File

@@ -1483,6 +1483,7 @@ static uint64_t submit(const uint64_t *handles, int n, const OwnCommands *own) {
VkCtx *c = vk_ctx();
plat_mutex_lock(&sub_lock);
vk_present_frames_owed(); // frames presented while another thread replayed close before this replay begins
if (n > 0 && !own) vk_sub_in_frame_publish(); // this submission's place in its frame, for the replay
vk_mem_dirty_submit(); // NX2NAVI_TRACE=perf: what a mirror would upload before this submit
uint64_t fence = __atomic_load_n(&issued, __ATOMIC_ACQUIRE) + 1;
cur_fence = fence;
@@ -1581,8 +1582,10 @@ int vk_sync_try_locked(void (*fn)(void)) {
return 1;
}
// vk_backend.c's, which runs the frames' bookkeeping (vk_frame_owed.h); builds without the back end have none.
// vk_backend.c's, which run the frames' bookkeeping (vk_frame_owed.h) and set the place in the frame of the submission
// being recorded; builds without the back end have neither.
WEAK void vk_present_frames_owed(void) {}
WEAK void vk_sub_in_frame_publish(void) {}
// st_lock held: p at the end of the queue *q.
static void pending_push(Pending **q, int *head, int *n, int *cap, Pending p) {