[perf] Runtime upload-strategy helper: direct HOST_VISIBLE|DEVICE_LOCAL vs staging (ReBAR-aware) #89
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Catcrafts/Crafter.Graphics#89
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
claude:done
Subsystem: Device / VulkanBuffer — upload strategy
Impact: Enabler (unifies the memory-placement cluster) · Effort: Medium
Depends on: #59 (GetMemoryType preferred/fallback) · Unblocks: #65, #72, #73, #75
Problem
Several issues want to put a CPU-written, GPU-read buffer "where the GPU reads it fastest." The right strategy is platform-dependent and currently each issue re-derives it:
DEVICE_LOCALheap isHOST_VISIBLE, so you map aDEVICE_LOCAL|HOST_VISIBLEallocation and write straight into VRAM — a staging buffer + copy is pure overhead.DEVICE_LOCAL|HOST_VISIBLEis a small (~256 MB) BAR window on a separate heap; allocating large buffers there competes for the window and throws (#58). There, staging into pureDEVICE_LOCALis mandatory.Verified on this dev machine (discrete GPU): memory type 4 is
DEVICE_LOCAL|HOST_VISIBLE|HOST_COHERENTon heap 0 — the full 24 GiB device-local heap (ReBAR on). The CPU/UMA device is a single 125 GiB heap with all flags. So on both, #73's proposed staging buffer would be wasted; but the decision must be made at runtime, not hardcoded.Proposed fix — runtime upload-strategy helper
Logic:
DEVICE_LOCAL|HOST_VISIBLEtype (via #59). None -> return false (must stage).barHeap = heaps[type.heapIndex].size;vram = largest DEVICE_LOCAL heap.barHeap >= ~0.9 * vram) -> true (direct map+write).barHeap << vram) -> true only for small/hot buffers under a budget threshold; false for large buffers -> stage.API shape — three outcomes, not two (caller must constrain the fallback)
A
booldirect-vs-stage return is insufficient. There are three outcomes, and which fallback is correct when no combined type exists depends on the caller's write pattern, which the helper does not know:allowStaging=true— staging intoDEVICE_LOCALis a clean one-time fallback.allowStaging=false— when no combined type exists they must fall back toPlainHostVisible, because a per-frame stage+copy defeats the purpose (and clobbers GPU co-writes for #65).HOST_CACHED).Ideally a thin abstraction in
VulkanBuffer(an upload-intentCreate/Resizethat hides the staging entirely from callers), so #65/#72/#73/#75 just declare intent and the buffer picks direct-vs-staged.Correctness caveats
VK_EXT_memory_budgetfor remaining budget on a small BAR window rather than total size, so a busy window is not over-committed.HOST_CACHED(type 3 here) — the ReBAR type is coherent-not-cached, so CPU reads from it are slow. Keep the two paths distinct.0 comments