- docs/profiling.md: new "Profiling the render path" section — headless
step() doesn't render; automation.screenshot() forces a render but PNG
encoding then dominates (~96% of instructions in libsfml stbi_zlib_compress),
burying engine work. Guidance: profile update/animation with step()-only,
subtract stbi_* for the render path. Alludes to the Hybrid Scene
Serialization wiki proposal (QOI + UI-tree serialization) as a faster
capture path that would also de-noise screenshot-driven profiling.
- tests/benchmarks/profile_workload.py: reusable full-loop driver (textured
grid + moving entities + animated nested UI, forced renders). This is the
workload that surfaced #348.
Refs #345, #348.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The default Release build (-O3 -DNDEBUG) omits frame pointers and ships no
DWARF, so it can't be line-annotated or unwound; build-debug (-O0) profiles
unoptimized code. Add a dedicated profiling variant for C++ hot-path work
(validating #331/#342/#343/#344).
- CMakeLists.txt: MCRF_PROFILE option adds -fno-omit-frame-pointer on top of
RelWithDebInfo (-O2 -g). Binary not stripped. Post-build lib/assets/scripts
copy already keys on the mcrogueface target, so build-profile/ self-populates.
- Makefile: `make profile` (-> build-profile/), `make callgrind SCRIPT=...`
(one-shot Callgrind on a headless benchmark), `make clean-profile` (wired
into clean-all).
- docs/profiling.md: full Callgrind + perf workflow, incl. the one-time
`sudo sysctl kernel.perf_event_paranoid=1` perf needs on this box.
- Pointers from the Makefile header and CLAUDE.md build section.
Verified: DWARF sections present, frame-pointer prologues emitted, headless
smoke test passes, Callgrind yields source line + call-count attribution, and
perf --call-graph fp resolves engine symbols.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>