Profile Newland CPU and GPU frame costs after AO optimization
Use the existing private offline harness to investigate broader CPU and GPU optimization opportunities in the fixed Newland Earsnest Forest scene, on the merged packed-thickness AO implementation. User authorizes investigation and implementation of evidence-backed optimizations, not commits or a PR in this turn. Keep the benchmark/private validation tooling out of team-facing changes unless explicitly requested; preserve original dirty benchmark worktree and unrelated captures in the main checkout. Scene: playfield567, anchor997.2/32.4/375.8 renderer XYZ; camera997.2/34.0/375.8, yaw0/pitch-15, hour14, procedural seed0,3840x2160,High grass and fixed graphics settings from Tools/ClientBenchmark/newland.json. Reuse existing offline loading, built-in profiling and Wayrender. Establish fresh baseline from origin/develop containing merged AO PR3021; compare capped and uncapped only where needed to separate waits/backpressure from useful CPU work. Existing static mode freezes simulation/render deltas and omits server entities, so conclusions are about this static render workload, not full live-game CPU behavior. Inspect CPU scopes, allocations/GC and GPU stages before selecting targets. Use sampled managed CPU/allocation profiling when aggregate scopes do not establish a cause. Do not sum overlapping scopes or call fence/pacing waits useful work. Preserve warmup/readiness, workload counters, instrumentation effects and GPU clock limits. Keep immutable baseline/candidate bundles; require matched reversed-order timing controls and output equivalence for retained changes. No density/sample/quality reductions or arbitrary culling heuristics. Acceptance: evidence-backed bottleneck breakdown with directly relevant source call chains; prioritize meaningful CPU and GPU candidates; retain only verified improvements with appropriate regression/real-render checks, or document concrete blockers and unverified hypotheses. Active worktree: /home/mathias/Projects/worktrees/engines-newland-profiling, perf/profile-newland-frame-costs, based on merged origin/develop c93ba9ced. Original dirty benchmark worktree and main captures preserved. Applied only private harness changes onto merged AO code; no commit/PR authorized. Fresh desktop baseline: useful CPU recording~2.8ms/frame and culling~1.2ms; waits shift between fence and pacing. Transparent2557draws from29meshes/14materials, plus reflection3284draws. Eight-second settled EventPipe sampled-thread/GC trace at managed-high:processCPU4.08s over collector8.178s; main-thread sampled wall attributes~1.13s to Monitor.Enter_Slowpath via DeviceMemoryAllocator.BorrowBuffer, mainly Mesh.RecordVulkan in transparency/reflection. All-thread percentages were misleading because idle workers dominate; per-thread attribution and built-in scopes are used instead. Sampling is wall-time, not native CPU counters. One actual Gen0 GC pause~.831ms; thousands of SuspendOther events are sampling suspensions, not GCs. Allocation samples primarily show hidden options Lua UI role/selection strings/delegates/enumerators and background LoadUploadBatch closures, not a demonstrated GPU/GC bottleneck. Candidate: replace allocator borrow lock/hash-set lookup with atomic owner lifecycle publication checks; keep allocation, mapped reads/writes, release/reclaim and statistics serialized. Owner states Unpublished/Active/Released/Reclaimed use volatile reads/writes; allocator disposal flag volatile. This avoids caching stale Vulkan handles and still rejects unpublished, wrong-allocator, released and reclaimed owners. Two controlled buffer/image liveness cases failed before the change because unrelated driver allocation blocked a live handle borrow; all74allocator tests now pass. Retained after fresh native-WSI controls: command-recording medians reference2.849442/2.999534ms versus borrow1.726214/1.746405ms,40.63% lower across two reversed-order run medians. Full4K output is pixel-identical. One unrelated final/fence run had broad CPU spikes across Update/Culling/Record/Pacing and remains preserved; no default2-slot FPS claim is based on it. User requests no more desktop popups. Stopped/rejected incomplete desktop comparison (second candidate run exited without profile.metadata.json; no four-run conclusion). Active private harness now defaults to Gamescope --backend headless --expose-wayland; removes host DISPLAY/WAYLAND_DISPLAY before compositor startup, unsets DISPLAY for child, verifies private Wayland output with wayland-info, records display-session metadata, and requires explicit --visible for desktop runs. No automatic desktop fallback or privilege escalation. Gamescope needs one private Xwayland instance even though the app uses native Wayland; --xwayland-count0 was rejected in smoke discovery. Ready-FD did not notify for native headless setup; initial safety timeout correctly terminated that attempt. Readiness now uses the child-confirmed private display after protocol/output verification. Completed full headless smoke at headless-wayrender-smoke:600valid4K frames, RX9070 RADV confirmed in Wayrender device metadata, gamescope-0, DISPLAY null, virtual90Hz, clean compositor exit0. GPU timings differ from desktop, so old and headless timing data remain separate. Regular-priority compositor may share GPU resources with desktop; no real-time capability or physical display-mode changes requested. Filesystem/audio/network are not sandboxed. Completed first headless A/B/A controls: all four runs valid/counters identical, command recording baseline2.9142/2.8641ms versus borrow-fast1.7307/1.7528ms (~40% lower); render excluding disjoint known wait spans4.6213/4.5510 versus3.5115/3.3867ms. Full-color4K image comparison has0changedpixels. However later validation identified an unrequested Gamescope implicit WSI interposer injecting invalid extensions/present structures; these datasets remain compatibility-layer-specific and are not native-WSI performance approval. Harness now disables ENABLE_GAMESCOPE_WSI and sets DISABLE_GAMESCOPE_WSI=1 for ALL application runs and tool inventory, never disables validation, and records native-Wayland WSI in display metadata. Fresh native controls are mandatory. GPU inspection with full pass statistics: main opaque681draws,1,644,475input triangles,2,494,459vertex invocations,50,042,399fragment invocations at4K; AO geometry repeats identical geometry with49,949,672fragment invocations. Reflection repeats opaque geometry at1920x1080 (8,369,572fragment invocations) and2557transparentdraws; main translucent2560draws/18,527,361fragment invocations. Invocation counts are not unique pixels or exact overdraw. Source confirms independent AO and scene depth targets; reusing depth would need correct sampling/attachment separation and position invariance, not a heuristic depth bias. These are GPU opportunities, not validated fixes. Confirmed second issue via new private counters PresentationImageIndex/RecordingFrameSlot:3images cycle against2frame slots, all117observed re-acquisitions in120frames referenced a fence since recycled for another image. Added ClearCompletedImageUses after successful per-slot fence completion, before reset/reuse, preserving genuinely pending image fences. One focused recycled-fence regression failed before the fix (old image retained fence1) and graphics suite now742passed/1pre-existing skipped. Native-WSI2-slot/4-image cycling naturally aligns slots, so no meaningful default2-slot frame-rate gain is claimed. In controlled native3-slot/4-image runs, update-to-pacing medians14.491383/15.220249ms before become11.097460/11.093630ms after (25.31% lower interval), reaching the virtual90Hz cap; ImageFence phases become absent while frame-slot waits remain. This is measured static headless cadence, not end-to-end latency or uncapped throughput. The three-slot4K full-color images are pixel-identical, and native shader-access/sync validation passes at3slots. Validation also conservatively tracked scratch writes in every dispatch of merged AO depth shader because binding7 was statically writable in its mip0 branch. Rather than disabling shader-access checks, split depth preparation/2x2 thickness packing into new ao_prepare_compute.glsl/default pipeline, leaving ao_depth_compute.glsl mip filtering only. Dispatch/image counts unchanged; existing release wildcard precompilers include the new compute source. Private Wayrender grouping now accepts prepare plus4mip-filter labels as depth stage. Retained: native reference/final and3-slot metadata sync/shader-access validation report no VUIDs/hazards, without the Gamescope WSI interposer. Original shared shader versus split preparation/mip implementation produces identical4K full-color images and801x601 Low/Medium/High AO-debug images (0changed pixels). Release wildcard baking generated504shader artifacts and the new preparation/mip/all-tier estimator SPIR-V passes spirv-val. No extra AO images, samples or filtering changes. Current setup: final bundle builds from active worktree with allocator fast path, fence-association fix and split AO prep; detached control worktree engines-newland-control at c93ba9ced carries the identical harness/AO-prep split but original allocator and no fence-clear call. Build native-reference, compose native-borrow by replacing only allocator DLL with final's (unchanged public API), and retain native-final; this isolates CPU borrow and fence bookkeeping without confounding the AO pipeline boundary. All native control/validation/image queues completed successfully. Added a separate metadata candidate after profiling allocation stacks: LuaUiPackageCatalog caches live PreferenceEntry references by role (not selected values), eliminating per-frame role-resolution closures/enumerators and preference-name rebuilding while retaining live updates; idle LoadUploadBatch now checks queues before entering a helper containing captured upload delegates. Across paired reversed controls, mean process allocations per frame9292.21/9405.57B become2999.80/2985.60B,67.99% lower; zero median counter deltas do not mean zero allocation. Follow-up EventPipe samples contain0LuaUiRoles.Get/ReadSelection/LoadUploadBatch allocation samples and no GC starts in8s; remaining samples identify idle ResourceManager publication lists and update delegates, not an approved zero-allocation claim. All metadata/full-color4K images equal the control. Final validation:allocator74passed,client4618passed/42pre-existing skips,graphics742passed/1pre-existing skip;14existing real-Vulkan upload/texture/mesh replacement/resize/rollback/lifetime cases passed. Hidden-window tests ran inside private Xwayland under the headless compositor, never the desktop; app benchmarks used native private Wayland. Production Release build passed0warnings/errors. Initial standalone shader-bake setup omitted a transitive GUI include; rerunning against the actual built shader directory fixed setup and all504artifacts bake successfully. No tests disabled. git diff --check passes. Evidence and copied logs: .dev-data/newland/verification/summary.json. User subsequently invoked /open-pr. Prepared isolated clean worktree /home/mathias/Projects/worktrees/engines-frame-overhead-pr on perf/reduce-client-frame-overhead from freshly fetched origin/develop27f0feff6. Carried only ten runtime/focused-regression files; benchmark, headless tooling, prior offline-viewer/sky-seeding changes and presentation diagnostic counters are excluded and remain local in original worktree. Four commits:8ba3e5898 allocator borrowing;e777e98c6 completed image fence references;1f4cf2b2a provider metadata/idle closures;4802a0c10 AO preparation/mip split. Branch pushed with own upstream. Isolated validation:allocator74passed,client4617passed/42skipped,graphics742passed/1skipped,14headless private-Xwayland Vulkan lifetime cases passed;production Release build0warnings/errors;504shader artifacts baked and AO preparation/mip/all-tier SPIR-V validated. Complete ten-file static feature review found no actionable findings; review itself did not execute code. Original main captures and dirty investigation worktrees preserved. Native AOT, other hardware/platforms, live server entities/advancing simulation and broad resolutions remain untested. Larger GPU depth-reuse/per-view/coverage opportunities are documented but not implemented. This completes the controlled investigation/optimization round, not broad release approval. Original baseline bundles remain immutable; trace baseline adds only a measured-frame stdout marker to align post-warmup EventPipe collection. Private tools support managed tracing, full Wayrender inspection, session metadata and collector-lifetime process CPU accounting, all separate from performance controls. All future GUI/Vulkan window tests must remain headless unless explicitly requested visible. No candidate is approved solely from compatibility-layer runs or screenshot timings. Pull request #3023: https://git.project-rk.com/prk/engines/pulls/3023 — perf/reduce-client-frame-overhead → develop. Open, not draft, no conflicts reported at creation. Existing completion state unchanged; no benchmark or profiling scaffolding included.
Checklist
3 of 3 complete- Establish matched merged-code baseline and separate useful CPU/GPU work, waits, allocations and GC with profiling evidence.
- Trace dominant costs to their actual source mechanisms and prioritize measured CPU and GPU optimization candidates.
- Validate retained optimizations against immutable controls, matching workload and output, and report limitations.
- Area
- Project Rubi-Ka
- Project
- engines
- Kind
- Research
- Task ID
tsk_er36tp8j3d- Updated