The fallback path committed the region but left it demand-zero (not resident). VirtualLock can pin only up to the process minimum working set, and the default quota is far below the 96 MiB region, so a bare lock fails with ERROR_WORKING_SET_QUOTA. Raise the minimum first via SetProcessWorkingSetSize, then VirtualLock; pre-fault every page as a last resort. Neither needs SE_LOCK_MEMORY (that is for large pages / AWE).
Append three host-readable sensor groups to the producer block, ABI
frozen by offset asserts (producer still fits page 0):
- cursor Tier-1 (hotspot, glyph dims, shape identity), published under
the existing cursor_seq gate
- content_change_ns: monotonic stamp of the last scene-content change
- display geometry on its own cache line under a geom_seq seqlock:
virtual-desktop bbox, captured-output origin, DPI, refresh
Source the cursor from each backend's existing grab metadata instead of
polling GetCursorInfo in the present pump: DDA from the duplication frame
info pointer position (no extra calls), NvFBC visibility from the grab
info plus one GetCursorInfo per frame for position, GDI from
GetCursorInfo. Position sampling now rides the frame rate, not the pump
tick.
Sample display geometry once at startup and re-sample only on backend
session recreate or a captured-mode change. Drop per-tick cursor
sampling (cursor_sample_pos) from the present loop.
Add src/stream/win32/geometry.{c,h}.
Append cursor_seq/cursor_visible/cursor_pos to the producer block
(host-RO ABI; offsets 168/172/176, existing layout unchanged). Position
is reported every pump tick, independent of draw_cursor, so a consumer
can overlay its own cursor even when the producer does not composite it.
x and y pack into one 8-aligned 64-bit field (single atomic store, no
tear); cursor_seq bumps last to gate a consistent read. Adds the
vgpu_publish_cursor() engine setter and a lightweight cursor_sample_pos()
(position/visibility only, no shape extract) called from the pump.