Repository navigation
feat(app-rules): INV-4.10 Vulkan frame-submit discipline — rule + lint + scaffolder (#1917) - #1942
Merged
Merged
Conversation
check_displayxr_app.py gains an advisory (WARN) check for the submit shapes that park a mobile GPU governor at ~1/3 clock: per enclosing function of each vkQueueSubmit (brace-matched, +-60-line fallback), and only when that function is on the frame path (draw / render pass / dispatch / xrWaitFrame-BeginFrame-EndFrame, or a render/draw/frame/present name), it flags (a) vkQueueWaitIdle/vkDeviceWaitIdle in the body, (b) vkWaitForFences after the submit on the fence it just passed, (c) vkAllocateCommandBuffers + vkFreeCommandBuffers in the same body, and as INFO (d) one un-indexed fence waited at the top and signalled by the submit (one frame in flight). One-off upload helpers are left alone; `// dxr-lint: allow-blocking-submit <reason>` on the submit or signature line exempts a deliberate blocking submit. Hermetic unit tests (positive Android/desktop shapes, VkFrameRing and hand-rolled-ring negatives, upload helper, exemptions) run in lint.yml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… linux/android New INV-4.10 in §4: >=2 frames in flight, one vkQueueSubmit per frame per queue, never wait the frame just submitted, no queue/device drains on the frame path, command buffers allocated once. Why (Adreno msm-adreno-tz DVFS, #1917; per-frame cmd-buffer alloc, #1935/#1936), evidence (modelviewer#170, 13 -> 30 fps), the sanctioned dxr::VkFrameRing pattern (displayxr-common >= v2.28.0), the allowed exceptions and the lint heuristic. Checklist row + a cross-reference from the android build guide's DVFS paragraph. /new-displayxr-app accepts platform=linux|android with the VK cube references mapped, warns not to copy the cubes' current submit loop, and the per-app CLAUDE.md template states INV-4.10. No test app is changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rule — INV-4.10 (new, in §4 Swapchain sizing & tiling)
Vulkan: keep ≥2 frames in flight, ONE
vkQueueSubmitper frame per queue, never wait the frame you just submitted. Also: novkQueueWaitIdle/vkDeviceWaitIdleon the steady-state frame path, and command buffers allocated once and reset, never alloc+free per frame.Why §4 and not a new §12: §4 is where the doc covers how the app renders into its swapchain (INV-4.4 tiling, INV-4.7 write every pixel, INV-4.8 per-slice depth clear), and how the atlas gets submitted belongs with those. §12 (checklist) and §13 (history) are already taken and have inbound anchors, so a new section would mean renumbering both. A checklist row is added to §12.
Why
Mobile GPU governors pick the clock from GPU busy %. A submit → wait → submit loop leaves the GPU idle while the CPU waits. On Adreno 740 (
msm-adreno-tz, ADPF unsupported) the GPU then sits near 220 of 680 MHz at ~10 fps (#1917; android-build-guide § Low frame rate with an idle-looking GPU). Allocating a command buffer every frame adds a zeroed allocation in the Adreno driver each frame (#1935 / #1936, the same fix already made invk_native). On a desktop dGPU the only cost is lost CPU/GPU overlap, which is why this survives desktop testing. Evidence: displayxr-demo-modelviewer#170 went from 13 to 30 fps on the phone, and the only change was 3 frames in flight with one submit per frame.The rule names one sanctioned pattern:
dxr::VkFrameRing(displayxr-commoncommon/vk_frame_ring.h, ≥ v2.28.0, DisplayXR/displayxr-common#74). The frame goesbeginFrame(), then the draws withtileBarrier()between tiles, thenendFrame()(the one submit), thenxrReleaseSwapchainImage, thenxrEndFrame. Exceptions: one-off uploads go throughsubmitImmediate(), resize and teardown usewaitAll(), and resources still in use go throughdeferDestroy().Lint heuristic + exemption (
scripts/check_displayxr_app.py, WARN, so CI is unaffected)For each
vkQueueSubmitin a.c/.cc/.cpp/.cxx/.mmsource, the check finds the enclosing function by brace matching: the innermost{…}whose header looks like a function, lambda or ObjC method. Control-flow blocks and initializers are skipped, as are braces inside string literals. If no function can be found, it uses a ±60-line window instead. The function is only judged if it is on the frame path, meaning it does at least one of:xrWaitFrame/xrBeginFrame/xrEndFrame;render(but notRenderer),draw,frameorpresentin its name.Any other function is treated as a one-off upload and is not reported. The swapchain acquire/release calls are deliberately not markers, because one-off static quad-layer fills make those calls too (that was a false positive on the Windows and macOS cubes). Inside a frame-path function it flags:
vkQueueWaitIdle/vkDeviceWaitIdleanywhere in the body;vkWaitForFencesafter the submit, on the same handle passed as the submit's fence;vkAllocateCommandBuffersandvkFreeCommandBuffersboth in the body;Exemption:
// dxr-lint: allow-blocking-submit <reason>on the submit line or the function's signature line. Known blind spots, also written into the rule: a drain hidden behind a helper in another function, and submit counts per frame.Tests:
scripts/tests/test_check_displayxr_app_vk_frame.pyhas 12 hermetic cases:record_atlasshape, the desktop wait-own-fence shape, and a frame-named function with no draw;VkFrameRing, a hand-rolled ring, a one-off upload, a*Rendererinit function, lazy alloc without free, commented-out code, and headers/JVM files;Mutation-checked: disabling the frame-path gate, the exemption, the fence match, the drain check or the alloc+free pairing each fails at least one test. The tests are wired into
lint.ymlnext to the INV-4.6 tests.Evidence: lint on the cubes today (WARNs are expected; that is the point)
The macOS
INV-5.9ERROR and theINV-9.1/INV-3.1WARNs are already onmain(checked by lintingmainwithout this branch). The only new findings are theINV-4.10lines.INV-4.10 findings across every
*_vk_*app (number of INV-4.10 lines):cube_handle_vk_android2 ·cube_zones_vk_android2 ·cube_handle_vk_win3 ·cube_zones_vk_win2 ·cube_zones_texture_vk_win2 ·cube_handle_vk_linux1 ·cube_zones_vk_linux2 ·cube_hosted_legacy_vk_{win,linux,macos}1 each ·cube_handle_vk_macos1 (INFO) ·cube_zones_vk_macos1 (INFO) ·weave_client_vk_android1 · probes (weave_*,windowspace_handle_vk_win) 0.Also in this PR
docs/getting-started/android-build-guide.md: the DVFS paragraph links to INV-4.10./new-displayxr-app:platform=linux|android(VK only), with the reference map pointing tocube_handle_vk_linux/cube_zones_vk_linux/legacy/cube_hosted_legacy_vk_linux/handle/cube_handle_vk_android/zones/cube_zones_vk_android;build_linux.sh --appslist, rootsettings.gradle);templates/app-CLAUDE.mdgains an INV-4.10 bullet.Local CI
All of the
lint.ymlPython steps pass locally:gen_adr_index --checkcheck_doc_pathsgen_extensions_index --checkcheck_cnsdk_pinsDeliberately NOT done
vk_frame_ring.h.Follow-ups
cube_*_vk_{android,win,linux,macos}(including the zones, texture and hosted-legacy variants) andweave_client_vk_androidtodxr::VkFrameRingafter displayxr-common v2.28.0 is tagged. The WARNs above are the checklist.🤖 Generated with Claude Code