mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Run a parallel group's pass hooks once, at its first chunk's first pass.
With Helpers take small jobs on (replay_small, with replay_pipe) the whole scene flashed for single frames under DLSS, with the interface intact. The pass hooks keep what the last pass that ran them left: the upscaler's light target is the one of the last scene pass before the output pass. A group's hooks run in its first chunk, and a pass that chunk began again (after a barrier, or a texture lookup that waited for its turn) ran them a second time. With the submitting thread already past the group, that came after the hooks of later passes and put the group's own target back, so the upscaler took an earlier image of the frame for that frame. The log showed it: the light target changed to another image and back at each flash. The rule is in runtime/vk_pass_hooks.h, with tests/pass_hooks_check, which fails against the old rule. Measured by replaying a bug capture in Kakariko (DLSS Performance, 3 workers, replay_pipe = 4, replay_small = 16, 100 s a run): 51 and 52 flash frames as installed, none with replay_small = 0, none with the helpers off, 52 with the render target reuse off. Two copies of the installed build made the same way, one with this change and one without: no flash frame in two runs with it, 373 in one run without. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
#include "vk.h"
|
||||
#include "vk_exec.h"
|
||||
#include "vk_evo_peek.h"
|
||||
#include "vk_pass_hooks.h"
|
||||
#include "vk_target_frames.h"
|
||||
#include "vk_dlaa.h"
|
||||
|
||||
@@ -818,8 +819,10 @@ void vk_exec_pass_begin(VkExec *ex) {
|
||||
resolve_rt(ex);
|
||||
if (vk_texture_deferred()) return; // a chunk's lookup waits for its turn (chunk_record): the op is replayed again
|
||||
// A parallel group's targets do not change: its pass hooks, which follow the frame pass by pass, run in its first
|
||||
// chunk, which the others start after (chunk_record).
|
||||
if (!rw_chunk || !rw_chunk->idx) {
|
||||
// chunk, which the others start after (chunk_record), and at that chunk's first pass alone: a pass it begins again
|
||||
// later may come after the hooks of passes further on in the stream (vk_pass_hooks.h).
|
||||
if (pass_hooks_due(rw_chunk != NULL, rw_chunk ? rw_chunk->idx : 0,
|
||||
rw_chunk ? __atomic_load_n(&rw_chunk->g->hooks, __ATOMIC_RELAXED) : 0)) {
|
||||
// the submitting thread past a group: the hooks read what its first chunk's left, and wait for the whole
|
||||
// group where they change what its draws read (vk_exec_hook_turn)
|
||||
if (!rw_chunk) vk_exec_hooks_wait();
|
||||
@@ -1929,8 +1932,9 @@ static void op_one(VkExec *ex, const op_hdr *h) {
|
||||
// group: up to N (at most LIVE_GROUPS) groups are handed out and not in their part yet, their chunks taken from one
|
||||
// queue in the order of the stream (Q). Hyrule Field, 3 workers (2026-11-12): the frame's groups took 6.8 ms of the
|
||||
// submitting thread and the ops between them 4.6. A group's chunk 0 runs its pass hooks after the group before began
|
||||
// its pass, and waits for every earlier group where a hook acts; a turn (vk_exec_turn) waits for every earlier chunk,
|
||||
// of earlier groups too.
|
||||
// its pass, once (a pass it begins again later would run them after those of passes further on, vk_pass_hooks.h), and
|
||||
// waits for every earlier group where a hook acts; a turn (vk_exec_turn) waits for every earlier chunk, of earlier
|
||||
// groups too.
|
||||
// The submitting thread waits for the groups (group_wait), and puts their command buffers and query slots in place
|
||||
// (group_drain, oldest first: groups_put_done), only where the order needs it:
|
||||
// - for room when N groups are out, before a part is split off or submitted (a part due before a group goes to the
|
||||
|
||||
19
runtime/vk_pass_hooks.h
Normal file
19
runtime/vk_pass_hooks.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Which pass begin runs the frame's pass hooks (vk_exec.c vk_exec_pass_begin: vk_contact_pass_begin, vk_dlaa_pass).
|
||||
// Header only, so that tests/pass_hooks_check.c checks the rule without a device.
|
||||
//
|
||||
// The hooks follow the game's frame pass by pass, and what they keep is the last pass's that ran them: the light
|
||||
// target the upscaler takes is the one of the last scene pass before the output pass (vk_dlaa.c vk_dlaa_pass). A pass
|
||||
// is begun more than once while it is recorded: a barrier, a texture lookup that waits for its turn or a pass of the
|
||||
// replay's own ends it, and the next draw begins it again. On one thread the second begin runs the hooks in the same
|
||||
// place of the stream, with the same targets, and changes nothing. A parallel group's pass is recorded by several
|
||||
// threads, and its hooks run in the group's first chunk. With the submitting thread going on past the group
|
||||
// (NX2NAVI_REPLAY_PIPE) a second begin in that chunk came after the hooks of passes later in the stream had run, and
|
||||
// put the group's own target back as the light target: the upscaler then took an earlier image of the frame for one
|
||||
// frame, a flash of the whole scene with the interface intact (Kakariko, DLSS Performance, 3 workers, 4 groups out:
|
||||
// 51 and 52 such frames in two runs of 100 s, none with the helpers off). So a group's hooks run once, at its first
|
||||
// chunk's first pass.
|
||||
#pragma once
|
||||
|
||||
// in_chunk: the calling thread records a chunk of a parallel group; idx: the chunk's place in its group; ran: the
|
||||
// group's hooks ran already (its first chunk began a pass).
|
||||
static inline int pass_hooks_due(int in_chunk, int idx, int ran) { return !in_chunk || (!idx && !ran); }
|
||||
Reference in New Issue
Block a user