feat(fps): show process CPU and memory in a System counter #562
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!562
Loading…
Reference in a new issue
No description provided.
Delete branch "mooki/Longshot:feat/system-readings-92"
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?
Type: feat · Risk: low · Tested on: Linux, RTX 4090 (one windowless game run and the game-free checks); Windows built, not run · Needs: Windows; AMD on Linux or Windows, and Intel; a quiet PC for the cost of System (T-3CTT, T-5ZH5, T-W43A)
Why
The FPS counter shows the renderer's work, but not how much processor time the game uses or whether the PC is running short of RAM, which is what a tester asks first about a stutter (#92). This adds a System choice to the FPS counter and an optional local readings file. It is the first slice of that issue; GPU utilization, temperatures and the busiest thread are not in it.
What is measured: one game run on Linux with an RTX 4090 (title screen, no window): the System counter drawn on screen, and a readings file of 48 rows whose values equal what the counter printed, picture by picture (12 of 12), with no fault in the log or the kernel. Game-free checks of the parsing, formatting, unknown values, the file and the menu's enable boundary, and a one-off native program that compared the Linux readers with second readings of the same quantities. Not measured: speed or the cost of sampling, Windows, AMD, Intel, the Steam Deck, a real window, longer runs, and the game run's own CPU, resident RAM and available RAM against an independent reader.
What changed
This adds a System level to the FPS counter, value 6 after Dev, that keeps FPS and frame time and replaces the graph and the renderer details with five lines: process CPU, resident RAM, available RAM, GPU heap used and GPU heap budget. It also adds an optional local readings file, a new versioned CSV written when
RX_VK_SYSTEM_LOGnames a path. The readings come from unprivileged Linux or Windows interfaces ingpu/vk/system_readings.cpp, an unavailable one showsn/aon screen andnain the file, never zero, and values 0 through 5 keep their meaning with the default Off.gpu/vk/fps_stats.happends System as value 6 after Dev. Values 0 through 5 keep their meaning and the default stays Off. System keeps FPS and frame time and replaces the graph and the renderer details with five lines: process CPU, resident RAM, available RAM, GPU heap used and GPU heap budget. It does not switch on Breakdown's extra timing.gpu/vk/system_readings.cppreads process CPU, resident memory and available physical RAM through unprivileged Linux or Windows interfaces.system_tick()ingpu/vk/vk_menu.cppadds the existing selected-device Vulkan usage and budget. A reading that is unavailable showsn/aon screen andnain the file, never zero.RX_VK_SYSTEM_LOG=<path>creates a new versioned CSV and refuses an existing file. Its columns aremonotonic_ns,cpu_process_pct,rss_bytes,ram_available_bytes,vram_used_bytes,vram_budget_bytes; its header is# longshot_system,1. There are no frame identifiers and no frame statistics in it.README.mdanddocs/development.mdexplain sources, units, unavailable readings and the cost of enabling it;host/hos_main.ckeepsRX_VK_SYSTEM_LOGout of the shared environment file, like the other switches that name a file to write.tests/test_system_readings.py,tests/test_system_readings.cpp). The FPS and environment-file fixtures keep their assertions; the latter also refuses a file-output path from an ordinary shared environment file.docs/tested/2026-11-14-system-readings-mooki-run01.md: the run below, as one row.Reviewer notes: Start with
system_readings::Sampler::tick, the strictproc_kibandcpu_percentroutines and thesystem_tickintegration ingpu/vk/vk_menu.cpp. The compact schema deliberately does not pretend to be the larger proposedperf.csvprotocol. Vendor sensors, per-field choices, busiest-thread readings, a background file writer and automatic report attachment are left for separate review on purpose.Related: #65 is a WIP memory and cache counter sampler with its own host callback; this change touches neither that interface nor the cache counters. Both use the driver's memory budget, so how they sample and write should be agreed before both land. The Dev level from #8 is kept as it is. The original request and plan were on the earlier tracker: https://github.com/NotRexed/Hookshot/issues/2915 and https://github.com/NotRexed/Hookshot/issues/2915#issuecomment-6834139903.
Overlaps (the forge's public pull request list, read at 12:24 on 2026-11-14, each diff applied to this head and to main with its context cut to one line): of the 103 open pull requests that change a file this one changes, three apply to main but not to this head. #62 has
names_fileinhost/hos_main.cas context, and this change addsRX_VK_SYSTEM_LOGto that line. #236 deletes the README sections, among them the FPS counter paragraph this change edits, so the System paragraph has to move with them. #244 inserts after theframe_generationrow of the options table indocs/development.md, where this change adds thefps_counterrow. All three are small; the second to land redoes its lines. Any pull request that adds menu text changes the same 13 language-count lines ingpu/vk/menu_i18n.has this one (two conflicts in the rebase were exactly there).Measurements
Verdict: the System counter and the readings file work in one Linux game run: 12 of 12 System pictures print exactly one file row each, in time order, from 48 rows with no fault in the log or the kernel, and the game-free checks pass (1064 assertions) and fail on main as expected (
FAIL: FPS counter value 6 is not System). This is not a speed comparison: with a level other than System and no readings file the new code does no clock read, no system read and no file write (counted: 0, 0 and 0); what System costs when switched on is not measured (T-W43A), and the run's CPU, resident RAM and available RAM were not compared with an independent reader.Branch state: on main
543846ebb08ded4ce75d364403bc4dcf24e83e85; head99904f1d8dfb2088b374560f1d19348f74ea0e17; whole game-free suite on this head:555 passed, 1 failed, 16 skipped. The 1 failing test fails on main the same way:tests/test_walk_key.py(a link error inread_input; fixed by #497).main:
543846ebb0(2026-11-14)this PR: feat/system-readings-92-m543846eb @
99904f1d8Hardware: RTX 4090 24 GB, NVIDIA 615.78.08, Core i9-13900K, 96 GB, Arch Linux 7.2.15
Scene: the title screen of a fresh profile (no save, no input), no window, output 1920x1080, pictures 1280x720; the counter set over the control bridge to Dev for 8 s, System for 12 s, then Off for 5 s with the file still on. Not a speed comparison
Method: one 53 s game run, built at
f7b4b4391(this PR's head99904f1d8differs only by thedocs/testedfile); the numbers come from the readings file the game wrote, its log and the pictures it captured; the other rows are game-free tests compiled with GCC 16.2.1 and Clang 23.1.3. An ordinary run: other work shared the PC (load average 8.0 before, 5.5 after), so no frame rate here is a measurementHow to reproduce: start the game with
RX_VK_SYSTEM_LOG=system.csvin the environment (the file must not exist yet), open the menu, Gameplay, FPS counter, System; the title screen is enough. For the tests,python -I -B tests/test_system_readings.py, and with--source <a checkout of main>for the control that fails on main543846ebb99904f1d8FAIL: FPS counter value 6 is not SystemPASS: System follows the existing Dev levelna, then 67.79 to 191.00% of one logical CPUThe off-path result counts operations, not elapsed time. No zero-cost or speed claim follows from it. Sampling and the optional flush happen on the menu tick's thread; enabling them can delay a frame, especially with slow storage.
How the counter was checked against the file: each of the 12 System pictures (one every 60 presents, about a second apart) was read by eye, its five values were written down, and each set of five equals one row of the file formatted the way the game formats it for the screen (whole MiB, CPU with two decimals). The first picture was taken 21 presents after the switch and shows the last row written in the Dev phase; the other 11 show System-phase rows. The four
vk: memory heapslines the game already logs (3732 of 22761 MB, then 3796 of 22762 MB three times) equal the file's heap pairs in whole MB. The game run's process CPU, resident RAM and available RAM were not compared with an independent reader such astop. Separately, a throwaway native program (not part of this change) called the branch'sread_processandcpu_percenton Linux while one or two threads burned CPU and 300 MiB was held, and compared them with second readings in the same process: CPU againstutimeplusstimeof/proc/self/stat(within 0.22 points: 99.65% against 99.50%, 199.55% against 199.49%, with GCC and with Clang), resident memory against therssfield of/proc/self/stat(within 1.6 MiB), andMemAvailableagainst a second parse of/proc/meminfo(equal). That covers the readers on Linux, not the numbers of the game run.The game folder and renderer were built natively with the project's updater (
compiled in 376s, 0 failed, versionv3.0.0-rc.3-80-gf7b4b4391, source stamp679585dd, a content fingerprint and not a commit). That is build success, not a build-speed comparison. No Windows game build is claimed.Screenshots
Picture 1 (
hookshot-system-readings-counter.jpg), this PR @99904f1d8, title screen, RTX 4090: left, the existing Dev level (the graph, the timing lines, VRAM3.7 of 22.2 GBand the renderer counts); right, System in the same corner: FPS and frame time as before, then Process CPU 137.35%, Resident RAM 3750 MiB, Available RAM 73032 MiB, GPU heap used 3796 MiB and GPU heap budget 22762 MiB. Look at the right side: five short lines instead of the graph and the renderer counts.Picture 2 (
hookshot-system-readings-over-time.jpg), this PR @99904f1d8: four System frames from the same run, each captioned with the line of the readings file it prints (lines 34, 38, 42 and 44, for example439301588217910,139.34,3952521216,76636049408,3980525568,23867686912for the first: 139.34%, 3769 MiB, 73085 MiB, 3796 MiB). Look at the values: they change from frame to frame and equal the file's rows.There is no main picture: main has no System choice.
Flags and settings
This PR adds a System level to the FPS counter (
fps_counter=6) and two switches,RX_VK_SYSTEM_LOGandRX_VK_SYSTEM_INTERVAL_MS; the counter stays Off by default and both switches are unset by default.fps_counter=6)fps_counter=6)RX_VK_SYSTEM_LOG=<csv>system.csvin the working directoryRX_VK_SYSTEM_INTERVAL_MS=<250..60000>Log lines the numbers come from
Testing
Tested: the new game-free fixture with GCC and Clang, the same compiled level check against main's source (it fails there, above), the existing FPS statistics fixture with both compilers, translation keys, counts and format conversions, README menu rows and the switch policy. The whole game-free suite on this head (
tests/run_all.py --jobs 6 --strict, Linux): 555 passed, 1 failed, 16 skipped in 158 s; the one failure,tests/test_walk_key.py, fails the same way on main (the same link error inread_input), and the 16 skips need Windows, a Vulkan device or the game's files. Seven Windows-target compilation calls pass on this head (syntax only: no Windows execution). A syntax-only compile of the mergedgpu/vk/vk_menu.cpppasses. In the game on Linux (RTX 4090, NVIDIA 615.78.08, no window): one run of the title screen with the counter at Dev, System and Off and the file on, described above. Two failures on the way were fixed before this head: a deprecated C++ filesystem conversion in the new reader (now the C++20 form), and the touched C environment fixture, which now defines the project's CRT-warning macro before its includes.Built for Windows and Linux at the current head, not run: At
99904f1d8dfb2088b374560f1d19348f74ea0e17, the Windows x86-64 Vulkan renderer DLL and all 50 selected host translation units were cross-built on Linux with llvm-mingw 20261104 (Clang 23.1.5), using the project's production flags and pinned dependencies (renderer: 36 newly compiled / 170 reused; host: 1 newly compiled / 49 reused; lifted C: 0 newly compiled / 80 reused; all Windows units: 37 newly compiled / 613 reused from matching object-cache entries), the renderer DLL linked and the full game executable linked; nothing was run on Windows or under Wine, and this is neither the project's hosted builder run nor a Windows UTF-8 run-time path test.The exact same head
99904f1d8dfb2088b374560f1d19348f74ea0e17also compiled and linked its full native Linux executable and renderer with Clang 23.1.3 through the documented resume path (323 newly compiled units, 169 matching Linux-cache units, source stamp679585dd), using the verified target-neutral C from lift5c175a4727d82f85e425e85400dca17fea0750fbrather than carrying the patch onto another commit; neither Linux binary was run.Not tested: Windows execution of the new C and C++ fixtures or of the game; AMD, Intel and the Steam Deck; a real window (swapchain presentation); low-memory conditions; captures longer than 53 s; translated layouts on screen (only English was looked at); full disks and slow or network storage; the game run's process CPU, resident RAM and available RAM against an independent reader (only the Linux readers were compared, in a separate program); the cost of enabling the sampler (no paired runs); the Dev picture against a main build (it was looked at on this build only). GPU utilization, temperatures and the busiest thread are not in this slice.
Must be tested:
n/awhere the driver has no memory budget and no shared-memory heap described as dedicated VRAM (test request T-5ZH5; by hand).Risks
What could break: CPU 100% is one logical CPU, so the process can exceed 100% (the run's maximum was 191.00%); it is not whole-PC use. Resident memory includes shared pages and is not private commit. The Vulkan budget is a moving allowance, not capacity, and an integrated GPU's device-local heaps can be system RAM. A sampled reading is context, not proof of a bottleneck.
While it is enabled the sampler reads a few small OS files and counters and flushes its optional file on the menu tick's thread, so it can add latency, and a pause delays the next sample.
Turning it off: Choose another counter level and leave
RX_VK_SYSTEM_LOGunset to stop sampling: the existing levels then do no clock read, OS read, formatting or file work, only an enable check. Reverting the change removes it. No synchronization or safety switch has to be turned off.Compatibility:
longshot_options.ini:fps_countergains the value 6 (System); values 0 through 5 keep their meaning and the default stays Off.RX_VK_SYSTEM_LOGis kept out of the shared environment file (host/hos_main.c), like the other switches that name a file to write.gpu/vk/vk_api.h, so it is unchanged.Issues
See #92
Checklist
Everyone
type(scope): description) of the status line's type.main.python -I -B tests/run_all.pypasses (any failure that also fails onmainis named).third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.docs/tested/for each new measured run (format indocs/tested-configurations.md; no appended row or assembled view).mainand passing here.tests/that reads changed source updated in this PR, not weakened.99904f1d8dfb2088b374560f1d19348f74ea0e17(under Testing); nothing was run on Windows at this head, so UTF-8 path behavior at run time is not claimed.New on this forge; not opened on GitHub.
WIP: feat(fps): show process CPU and memory in a System counterto feat(fps): show process CPU and memory in a System counterBuilt at the current head
99904f1d8dfb2088b374560f1d19348f74ea0e17for Windows (llvm-mingw cross-build) and Linux (native); both linked without errors, nothing was run. The build box is ticked for this head and the details are under Testing. Every box is done, so this is ready for review.I think it's better to added to other modes rather than exclusive standalone mode.
View command line instructions
Manual merge helper
Use this merge commit message when completing the merge manually.
Checkout
From your project repository, check out a new branch and test the changes.