mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 01:41:02 +00:00
Write back GPU renders into textures that share their page when the texture's own bytes are unchanged (#13, #63, #118, #252)
The page write tracking dropped the render of any texture smaller than a page whenever the CPU wrote another part of that page. The Purah Pad camera renders the ID, screen rect and depth of what is under the reticle into 4x4 targets in two slots and reads them a frame later. On 1.4.3 one slot's rect target shares its page with data the CPU writes every frame, so the game read a zero rect every other frame and the target's name label blinked; on 1.2.1 the targets of both slots were dropped and nothing was recognised. A texture that fills no page now keeps a copy of its storage bytes from the GPU write, and a page write counts only when those bytes changed. TOTK_WB_EXACT=0 restores the old decision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7d62cbfa85
commit
de3ed42a4c
2 files changed
+32
-5
No files matched your search
+3
-3
@@ -50,7 +50,7 @@ static const char *const known[] = {
|
||||
"SHADER_MEMO", "SHARED_FONTS", "SHARPNESS", "SHOTS", "SHOTS_DIR", "SHOTS_FULL", "SPEED_PROBE", "SPIN", "SPIN_HANDOFF_US", "SPLIT_DRAWS", "SPLIT_US", "STICK_DEADZONE", "SWKBD_ANSWER",
|
||||
"SWKBD_TEST", "TEST_PAD", "TEST_PREPARE_SHADERS", "TEX_ALIAS", "TEX_MEMO", "TEX_VRAM_PRESSURE", "TIMER_1MS", "TRACE", "UBO_SNAPSHOT", "UPSCALER", "VK_DYN_A2C", "VK_DYN_VERTEX", "VK_NO_FLOAT64",
|
||||
"VK_PRESENT", "VK_RECORDER", "VK_SSBO_ALIGN", "VK_SUBGROUP", "VK_SUBGROUP_PIN", "VK_VALIDATION", "VOLUME", "VRAM_HEADROOM", "VS_OUTS",
|
||||
"WB_FENCE_LIMIT", "WINDOW_SIZE", "XLATE", "XLATE_ID", "XLATE_PROCS", "ZSTD_CHECK", "ZSTD_NATIVE",
|
||||
"WB_EXACT", "WB_FENCE_LIMIT", "WINDOW_SIZE", "XLATE", "XLATE_ID", "XLATE_PROCS", "ZSTD_CHECK", "ZSTD_NATIVE",
|
||||
};
|
||||
|
||||
// The totk.ini section a key cfg_set may write goes to when the file lacks it (totk.ini.example's sections).
|
||||
@@ -80,7 +80,7 @@ static const struct { const char *key, *section; } sections[] = {
|
||||
{"PUSH_SKIP", "performance"}, {"SPLIT_DRAWS", "performance"}, {"SPLIT_US", "performance"}, {"VK_VALIDATION", "performance"},
|
||||
{"VK_DYN_VERTEX", "performance"}, {"VK_DYN_A2C", "performance"}, {"VK_SUBGROUP_PIN", "performance"},
|
||||
{"TRACE", "diagnostics"}, {"FLIGHT", "diagnostics"}, {"FLIGHT_SPIKE_MS", "diagnostics"},
|
||||
{"WB_FENCE_LIMIT", "diagnostics"},
|
||||
{"WB_FENCE_LIMIT", "diagnostics"}, {"WB_EXACT", "diagnostics"},
|
||||
};
|
||||
|
||||
// The settings the menu shows: the value in effect when the key is unset, and what the key accepts. The only place a
|
||||
@@ -131,7 +131,7 @@ static const CfgMeta metas[] = {
|
||||
{"VK_DYN_A2C", "", "0|1", RESTART}, {"VK_SUBGROUP_PIN", "1", "0|1", RESTART},
|
||||
// diagnostics
|
||||
{"TRACE", "", NULL, RESTART}, {"FLIGHT", "0", "0|1", RESTART}, {"FLIGHT_SPIKE_MS", "100", "0..60000", RESTART},
|
||||
{"WB_FENCE_LIMIT", "65536", "0..1073741824", RESTART},
|
||||
{"WB_FENCE_LIMIT", "65536", "0..1073741824", RESTART}, {"WB_EXACT", "1", "0|1", RESTART},
|
||||
{"INPUT_RECORD", "", NULL, RESTART},
|
||||
};
|
||||
|
||||
|
||||
+29
-2
@@ -55,6 +55,7 @@ typedef struct Tex {
|
||||
uint64_t wr_fence; // any entry: newest submission recorded to write its image (render, copy, clear,
|
||||
// storage binding); ReadTexels waits for it instead of draining the replay queue
|
||||
uint8_t listed; // in owned[]
|
||||
uint8_t *snap; // a texture that fills no page: its storage bytes when the GPU write was recorded
|
||||
uint32_t wt_n; // WriteTexels into the storage since the last upload
|
||||
struct Tex *rel_prev, *rel_next; // while released: the list of released entries, oldest first
|
||||
} Tex;
|
||||
@@ -507,6 +508,7 @@ static void destroy_tex(Tex *e) {
|
||||
if (e->released) nreleased--, rel_unlink(e);
|
||||
vk_exec_image_gone(e->pub.image);
|
||||
vk_defer_image(e->pub.image, e->alloc);
|
||||
free(e->snap);
|
||||
free(e);
|
||||
}
|
||||
|
||||
@@ -727,12 +729,33 @@ static void upload(Tex *e) {
|
||||
perf_end(PERF_TEX, p0);
|
||||
}
|
||||
|
||||
// Whether e's storage fills no 4 KiB page of its own, so the write tracking can only see writes to pages it shares.
|
||||
static int fills_no_page(const Tex *e) {
|
||||
return ((e->pub.addr + e->t.L.size) & ~0xfffull) <= ((e->pub.addr + 0xfff) & ~0xfffull);
|
||||
}
|
||||
|
||||
// A texture that fills no page keeps a copy of its storage bytes from when a GPU write to it was recorded
|
||||
// (vk_texture_gpu_written), up to SNAP_MAX bytes; wb_exact (TOTK_WB_EXACT, default on) enables it.
|
||||
enum { SNAP_MAX = 4096 };
|
||||
static int wb_exact = -1;
|
||||
static volatile int32_t n_kept;
|
||||
|
||||
// vk_mem_changed over the pages wholly inside e's storage (all of its pages when it fills none): a neighbour that shares
|
||||
// the first or last page does not count as a write to e.
|
||||
// the first or last page does not count as a write to e. A texture that fills no page shares all of its pages, so
|
||||
// there a page write counts only when e's own bytes differ from the copy taken at the GPU write. TotK's Purah Pad
|
||||
// camera renders the ID, screen rect and depth of what is under the reticle into 4x4 targets of 512 bytes, two
|
||||
// slots used in turn, and the CPU reads them a frame later. One slot's rect target shares its page with data the CPU
|
||||
// writes every frame: without the copy its renders were never written back, the game read a zero rect every other
|
||||
// frame and the target's name label blinked. TOTK_WB_EXACT=0 restores the page-only decision.
|
||||
static int storage_rewritten(const Tex *e, uint32_t since, uint32_t *now) {
|
||||
uint64_t lo = (e->pub.addr + 0xfff) & ~0xfffull, hi = (e->pub.addr + e->t.L.size) & ~0xfffull;
|
||||
if (hi <= lo) lo = e->pub.addr, hi = e->pub.addr + e->t.L.size;
|
||||
return vk_mem_changed(lo, hi - lo, since, now);
|
||||
int ch = vk_mem_changed(lo, hi - lo, since, now);
|
||||
if (ch != 1 || !since || !e->snap || memcmp(e->snap, (const void *)(uintptr_t)e->pub.addr, e->t.L.size)) return ch;
|
||||
log_limited(&n_kept, "write-back of %#llx kept: fmt %#x %ux%u, %llu bytes; a neighbour wrote its page",
|
||||
(unsigned long long)e->pub.addr, e->t.d.format, e->t.d.width, e->t.d.height,
|
||||
(unsigned long long)e->t.L.size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// A mirrored texture is uploaded on first use and again when its storage pages carry a newer write generation.
|
||||
@@ -947,6 +970,10 @@ void vk_texture_gpu_written(const tex_t *t) {
|
||||
if (e->pub.mirrored) {
|
||||
e->gpu_dirty = 1;
|
||||
storage_rewritten(e, 0, &e->gpu_gen); // CPU writes up to here are older than this render
|
||||
if (wb_exact < 0) wb_exact = cfg_bool("TOTK_WB_EXACT");
|
||||
if (wb_exact && !e->snap && e->t.L.size <= SNAP_MAX && fills_no_page(e) && !(e->snap = malloc(SNAP_MAX)))
|
||||
rt_fatal("vk: out of memory for a texture's storage copy");
|
||||
if (e->snap) memcpy(e->snap, (const void *)(uintptr_t)e->pub.addr, e->t.L.size);
|
||||
e->gpu_seq = ++gpu_writes;
|
||||
e->gpu_fence = vk_exec_recording_fence();
|
||||
wb_cfg_init();
|
||||
|
||||
Reference in new issue
Block a user