feat(engine): the single compute dispatch runs on the same machinery as a batch - #1912
Conversation
…as a batch bind_and_dispatch_compute_kernel now builds a BatchedComputeKernelDispatch of one and hands it to GpuContext::dispatch_compute_kernel_batch: barriers and dispatch land in one recording on the shared cached recorder — one submission, one fence wait — instead of a throwaway transition recorder's submit-and-wait followed by VulkanComputeKernel::dispatch's drain, submit, and post-wait. The kernel fence's fail-then-hang flaw (a failed submit leaves it unsignaled forever) leaves this path with it. batched_binding_location names the dispatch index only when the recording holds more than one, so a single dispatch's refusals never say 'dispatch 0 of this batch'. The batching cost test's separate arm tightens to exactly N submissions and N stalls; a new test pins the single op at 1/1 and asserts the tracked resting layouts (falsified against the kernel-fence path: stalls read 2). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ath trim Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tch tails Review round 1. The batch machinery published tracked layouts per first-touch registration per image, but a cross-process resolve mints a fresh registration cell per call — one image bound at two same-kind slots (which only kind clashes refuse) left the second cell stale, and the surface-share dedup keeps exactly that cell. The publish now walks every binding; falsified by narrowing the walk (second cell reads UNDEFINED vs GENERAL). Also from review: the two dispatch paths share compute_bound_surface_layout_publish_pairs and dispatch_compute_recording_and_publish_bound_surface_layouts instead of verbatim tails; batched_binding_location becomes binding_location_in_this_recording taking the recording slice; dispatch_compute_kernel_batch's doc stops attributing the separate-dispatch cost to the kernel fence; push constants move instead of copying; seeded_chain_textures takes the count it is asked for; new test proves a single-dispatch refusal inside the recording leaves the shared recorder usable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…only batches Review round 2: the recorder field doc still called it batch-only — the exact divergence this branch removed — and seeded_chain_textures' expect message hard-coded the old three-texture count. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughChangesSingle compute dispatches now use the shared batch recorder as a batch of one. Image layouts are tracked by Vulkan image and published to all matching registrations. Tests cover submission counts, recorder recovery, and layout publication. Compute dispatch unification
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: ⚪ Minimal · up to The single-dispatch path now shares the batch recording and publication machinery, with validated submission, layout-tracking, refusal, and cost behavior. No actionable merge-blocking risk remains after normal checks and review. Sequence Diagram(s)sequenceDiagram
participant Client
participant EscalationHandler
participant SharedRecorder
participant GPU
participant SurfaceShare
Client->>EscalationHandler: run_compute_kernel
EscalationHandler->>SharedRecorder: record one compute dispatch
SharedRecorder->>GPU: submit barriers and dispatch
GPU-->>SharedRecorder: complete submission
SharedRecorder-->>EscalationHandler: return after one wait
EscalationHandler->>SurfaceShare: publish landed image layouts
SurfaceShare-->>Client: complete operation
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
The two compute-dispatch escalate ops now reach the GPU through one machinery.
run_compute_kernelbuilds aBatchedComputeKernelDispatchof one and ridesGpuContext::dispatch_compute_kernel_batch: barriers and dispatch land in one recording on the engine's shared cached recorder — one submission, one fence wait — replacing a throwaway per-dispatch transition recorder (own command pool, own fence, own submit-and-wait) followed byVulkanComputeKernel::dispatch's drain, submit, and post-wait. A dispatch whose bindings needed a transition paid 2 submissions and 3 stalls; one whose bindings sat right paid 1 and 2. Both are now 1 and 1, counted, not timed.The kernel fence's fail-then-hang flaw leaves this path with it: a failed
vkQueueSubmitleaves that fence unsignaled forever and wedges the next dispatch; the recorder tracks in-flight state so it cannot.Through the batch method, never the batch handler — a single dispatch's refusals keep naming the binding, never "dispatch 0 of this batch" (the ticket's ruled-out shape). The one message inside the method that carried batch vocabulary now names the binding alone when the recording holds one dispatch.
Review round 1 surfaced a real narrowing and it is fixed in the shared machinery: the batch path published tracked layouts per first-touch registration per image, but a cross-process resolve mints a fresh registration cell per call — one image bound at two same-kind slots (only kind clashes are refused) left the second cell stale, and the surface-share dedup kept exactly that cell. Publication now walks every binding, so every cell naming an image learns the layout the recording left it in; the batch op inherits the fix. Falsified: narrowing the walk fails the new test with
UNDEFINEDvsGENERAL.Outside tests the sources are +136/−124 (the extracted shared helpers and the per-binding publish carry docs); the wheel diff is two comments that claimed the single op dispatches on the kernel's own fence.
Closes
Closes #1890
Exit criteria
Test plan
Every gate green at HEAD:
cargo fmt --check, CI's workspace clippy (no warning in any changed hunk),cargo xtask run-local-ci-gates,cargo test -p streamlib-engine --lib(1351 passed),-p streamlib-python-wheel --lib(72),-p xtask(226),cargo doc -p streamlib --no-depswarning-free.Engine, on the rig (RTX 3090) —
compute_kernel_dispatch30 pass (3 new),graphics_kernel_dispatch25,ray_tracing_kernel_dispatch25:a_single_dispatch_costs_one_submission_and_one_stall_and_rests_its_layouts— warm-up then one measured op via the device's submission/fence-wait counters, plus the tracked resting layouts (sampled →SHADER_READ_ONLY_OPTIMAL, storage →GENERAL) and the output pixels. Falsified — restoring the kernel-fence path fails it: stalls 2 vs 1.every_registration_cell_naming_one_image_learns_the_landed_layout— two hand-minted registration cells over one image (the shape a path-2 resolve produces), both assertedGENERALafter the recording. Falsified — narrowing the publish walk to first-touch leaves cell B atUNDEFINED.a_single_dispatch_failing_inside_the_recording_leaves_the_recorder_usable— undeclared push constants refuse atset_push_constants, after the barriers are recorded; the next dispatch succeeds. The single-path analogue of the batch's abort test.a_batch_costs_one_submission_and_one_stall_where_separate_dispatches_cost_n— separate arm tightened from>=to exactly N submissions and N stalls.Python, on the rig —
test_compute_kernel.py+test_kernel_dispatch_batch.py: 21 pass after amaturin develop --releaserebuild.Reviewed by
review-pr(two rounds) andrust-craftsmanship-reviewer; every should-fix from both is in this diff.Notes for owner
VulkanComputeKernel::dispatchkeeps its fail-then-deadlock fence (verified in review:reset_fencesthen any failure before/at submit leaves it unsignaled; the next call blocks forever) and the tone mapper, both colour-converter paths, andsdk/vulkan-jpegstill call it. Engine-layer defect; it retires naturally with the already-anticipated follow-up migrating those callers as part of the plan's bindings-at-dispatch convergence. Only reachable on a failed submit, so not filed as blocking.wait_for_completionthe local registration cells advance (deliberately — the queue owns the transitions once submitted) but the surface-share store is only updated on an Ok escalate, so a failed wait leaves store and cells divergent. Unchanged by this diff; noting for whoever next works on cross-process kernel bindings.binding_location_in_this_recordinghas test coverage. Left as-is rather than adding a contrived direct-call test.docs/testing-hardware.mdhas drifted: all five crates in its tier-1--excludelist are gone from the workspace (gate-runner finding, unrelated to this diff).#[cfg(target_os = "linux")], and--target aarch64-apple-darwinremains broken on this box by the pre-existing iceoryx2libproc.hfailure.🤖 Generated with Claude Code
Summary by CodeRabbit