Claude/snap precopydata registry wsr7hr - #91
Merged
Merged
Conversation
For an indexed triangle list, the maximum unique verts a draw can touch is prim_count * 3. When compute_prim_vert_count derives num_vertices from the full bound VB (vb_size / vert_size) it can vastly exceed that bound, which is a strong signal the VB is shared across many draws or the geometry is CPU/software-animated and not usefully snapshottable. It also tends to coincide with a missing-index-buffer error. Log a warning in snapshot::take so users get a clear in-log hint instead of a confusing error chain. No behavior change; diagnostic only. Authored by Claude (claude-opus-4-7)
Character-mesh parts packed into a large dynamically-updated vertex/index buffer (a "megabuffer") failed to snapshot, logging "failed to get index buffer data, was not previously saved" and a vertex count equal to the whole bound buffer rather than the draw's range. Two native fixes (DX11 only; managed/interop unchanged, no version bump): 1. Capture buffers filled after creation. hook_CreateBuffer only copied data supplied as pInitialData. Now, when precopy is enabled, also hook ID3D11DeviceContext::Map/Unmap and UpdateSubresource to copy index and vertex buffer bytes whenever the game fills them. CreateBuffer records per-buffer metadata (type + size) so the new hooks can identify tracked VB/IBs cheaply; a createtime entry is pushed only on new keys so the expiry GC stays correct and continuously-updated buffers self-heal. 2. Snapshot only the draw's sub-region. set_buffers_d3d11 assumed per-mesh buffers. It now slices the prim_count*3 indices starting at start_index (honoring bind offsets), finds the referenced vertex range, carves out those vertices, and re-bases the indices to 0 so managed reads a self-contained mesh from offset 0 (its tested path). num_vertices, base_vertex_index, min_vertex_index and start_index are set accordingly. Authored by Claude Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DsyY4xhCQs2tQ1f3BkmDTL
Authored by Claude (claude-opus-5) Builds on the preceding commit, which could capture and slice buffers the game fills after creation but was not usable in practice: it could not be turned on without restarting the game, it made the game unplayably slow once on, and when it did produce a snapshot there was no way to tell a good one from a garbage one. Introduces "dynamic buffer" as the shorthand for an index or vertex buffer whose contents we get by watching the game write it, rather than from the pInitialData it was created with. Gated behind a new `snapshot-dynamic-buffers` Cargo feature, off by default, since snapshotting is still slower on this path than the plain whole-buffer one and the path it replaces has stricter size checks. Declared on hook_core, propagated to hook_snapshot and shared_dx. With it off the DLL behaves as it did before this work: the write hooks move to a gated hook_dynamic_buffers module and are not installed, their fn pointers leave HookDirect3D11Context (which is Copy and returned by value on every draw), hook_CreateBuffer records no metadata, and set_buffers_d3d11 hands over whole bound buffers with the original checks. Enabling precopy in game now works. The write hooks decided whether a resource was a tracked VB/IB by consulting metadata only hook_CreateBuffer writes, and that is only hooked when precopy was already on when the device was hooked -- so every buffer the game had already created was invisible, which is precisely the long-lived megabuffers this exists for. They now resolve unknown resources themselves via GetType/GetDesc and cache the answer, negative results included so that constant buffer writes stay cheap. Capture runs only while a snapshot is in progress rather than for the rest of the session. The cost is one copy of an entire buffer per write, and a game may write one many times per frame, which is the difference between slow and seconds per frame. SnapPreCopyAlways=1 restores always-on capture. The copy also reuses its destination allocation: at these sizes the allocator returns large blocks to the OS, so a fresh Vec per write meant faulting in new zero pages every time, costing more than the copy. Fixes a snapshot window that could fail to terminate. An earlier attempt here discounted capture time from the window's elapsed wall clock so a slow captured frame would not expire it; that is a feedback loop, since capture only runs while the window is open, and a frame that is nearly all capture advances the clock by nearly nothing. The window is wall clock again; snap_ms is the knob for making it longer. Adds a capture-provenance diagnostic, since a megabuffer part sometimes snapshots as poly soup and nothing in the log distinguished a stale copy from a current one. Each captured buffer records the write path that last produced its bytes (including the D3D11_MAP type), a global capture sequence number, a capture count and the stored length; the index and vertex buffer's are logged at snapshot time. Comparing the two sequence numbers says whether the pair came from the same batch of writes. Holding shift with the clear-texture-lists key drops everything captured from a dynamic buffer. These maps are keyed on raw buffer pointers, nothing hooks buffer Release, and the capture paths' staleness guards only run when a capture happens -- so after a scene change a snapshot can read bytes that no longer describe what the draw will read. Buffers filled at creation are kept, since DX11 will not read a buffer back and they are not affected. The two are told apart by the provenance above. The DX11Metrics counters this adds are named dyn_precopy_* and report under "dyn precopy:", to mark them as measuring only the atypical dynamic path; they stay zero in a default build.
- Add some comments to devnotes on top of claude's thing which is mostly about details - Update some of the code comments that claude added
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.