mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Shadow storage noted a GPU write with the whole range its binding names: the copy-in skipped that range and the fence copied all of it back, so CPU writes to other fields of the same records never reached the GPU. Ocarina's lighting parameter is such a field: the lighting came out NaN and the fog pass drew the scene black. Each page a noted range covers now keeps a base copy, the copy-in moves only the bytes the CPU changed and the write-back only those the GPU changed (vk_memory.c merge_page). The mirror's merge shader marks the 4 KiB pages it changed, and one job per submission copies only those back (shadow_wb_cb); the copy-in reads the CPU-side generation tables through the shared write-watch snapshots; and a caller that reads guest memory itself resolves with vk_mem_lookup, which does not queue the pool for the copy-in. Pull request #394 by jacc. Its changes are taken as they are; one hunk met the lazy write-back that came later (vk_gpu_written_back) and is merged by hand. Added here: the lazy write-back stays off with shadow storage, because its fetch copies the mirror into the pool's buffer, which is guest memory only for an imported pool. Files other sessions are changing: runtime/vk_memory.c (the shadow section, the merge record, vk_gpu_written_back, wb_lazy) and three one-word call changes in runtime/vk_usnap.c. Without shadow storage (Windows outside Wine) the merge pipeline gains an unused fifth binding and two push constants and nothing else changes. The Windows files compile; nothing was run, on Windows, Wine or Linux. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
35 lines
1.7 KiB
Plaintext
35 lines
1.7 KiB
Plaintext
#version 450
|
|
// Write-back merge of a VRAM mirror (vk_memory.c, wb_merge_record): copies a 32-bit word of what the GPU left in the
|
|
// mirror into the pool's imported buffer (guest memory) only where it differs from the snapshot taken before the
|
|
// submission's GPU work, so words the GPU did not change keep what the CPU wrote meanwhile.
|
|
// One invocation per word of the listed ranges; ranges are sorted by their first invocation (x: word offset in the
|
|
// mirror and imported bindings, y: word offset in the snapshot binding, z: words, w: first invocation).
|
|
// With track set (shadow storage) a word that changed also marks its 4 KiB page of guest memory in Pages: page0 is the
|
|
// bindings' start within its page, so the CPU's write-back at the fence reads only the marked pages of the shadow
|
|
// (vk_memory.c, shadow_wb_cb).
|
|
layout(local_size_x = 64) in;
|
|
layout(std430, set = 0, binding = 0) readonly buffer Post { uint post[]; };
|
|
layout(std430, set = 0, binding = 1) readonly buffer Pre { uint pre[]; };
|
|
layout(std430, set = 0, binding = 2) buffer Dst { uint dst[]; };
|
|
layout(std430, set = 0, binding = 3) readonly buffer Ranges { uvec4 rng[]; };
|
|
layout(std430, set = 0, binding = 4) writeonly buffer Pages { uint pages[]; };
|
|
layout(push_constant) uniform Push { uint nranges, total, page0, track; } pc;
|
|
|
|
void main() {
|
|
uint i = gl_GlobalInvocationID.x;
|
|
if (i >= pc.total) return;
|
|
uint lo = 0, hi = pc.nranges;
|
|
while (hi - lo > 1) {
|
|
uint m = (lo + hi) >> 1;
|
|
if (rng[m].w <= i) lo = m;
|
|
else hi = m;
|
|
}
|
|
uvec4 r = rng[lo];
|
|
uint k = i - r.w;
|
|
uint v = post[r.x + k];
|
|
if (v != pre[r.y + k]) {
|
|
dst[r.x + k] = v;
|
|
if (pc.track != 0u) pages[(pc.page0 + (r.x + k) * 4u) >> 12] = 1u;
|
|
}
|
|
}
|