Repository navigation
Conversation
`CAP_GPUI_AUTO_TIMELINE_BENCH=1` times a synchronous `Window::draw` of the editor the way `camera_bench` does for the camera bubble, after the `CAP_GPUI_AUTO_EDITOR` project is open and its waveforms have landed. It reports mean / p50 / p95 / max draw time for five scenarios: playback at the default zoom, playback zoomed out to the whole clip, a paused hover sweep, a zoomed-in following playback, and a clean draw with nothing notified. The playing scenarios dirty the timeline through the production `invalidate_playback_chrome`. The draw-and-report helpers move out of `camera_bench` into `draw_bench` so both benches share them.
…ints The timeline repaints on every playhead tick during playback, and each repaint rebuilt the mic and system waveform fills with lyon even when their inputs had not changed. Zoomed out to a whole 180 s clip the two fills tessellate to about 34,000 vertices, which cost the editor around 16 ms per draw on `main`, a full 60 Hz frame budget. `waveform_path` now takes one `WaveformGeometry` value, which doubles as the key of a `WaveformPaths` cache on `TimelineModel`: one slot per clip segment and track, compared on every builder input with the peak table matched by `Arc` identity, swept each render so only last frame's visible paths are kept, and carried across `rebuild_timeline` like the waveforms. The paint closure clones the stored path on a hit; colour, opacity, mask and DPI scale are still applied by `paint_path` per paint. Once the view follow-scrolls, the range changes every tick and the cache misses, so scrolling playback costs what it did before. Timeline draw benchmark, 180 s clip, 5 interleaved runs, median p50: playing at the default zoom before the view follows 1.12 ms -> 0.70 ms, playing zoomed out 15.7 ms -> 0.80 ms, paused hover zoomed out 16.1 ms -> 0.96 ms, scrolling playback (every draw misses) 1.39 ms -> 1.36 ms.
… in place The Linux Instant camera compositor converted every screen frame to RGBA and back, and rebuilt the camera overlay and its mask, just to blend a small camera rectangle. Blend directly into packed RGB screen frames, rebuild the overlay only when a new camera frame arrives, and compute the mask once. The old conversion path remains the fallback for other formats.
…rame ReadSample hands back a null sample for stream ticks (gaps in the stream), and read_sample reported those as Ok(None), the same as the end of the stream. The decoder then stopped a batch early and answered with an older frame. Read again until a sample, the end-of-stream flag or an error.
…est state machine The decoder thread mixed batching, a mid-batch readahead slide, deferred requests and four fallback chains in one loop. The policy now lives in FrameRequests, which never touches Media Foundation: the thread takes in requests, asks it for the next step (seek, decode one sample, or wait) and does it. Tests drive it with a fake sample stream. Once the decoder moves past a requested frame, the request gets the newest sample at or below that frame. Requests are served lowest first, each with its own seek when decoding forward cannot reach it, so the "first sample after" fallback only applies to a frame before the stream's first sample. The cache keeps every sample since the last seek and evicts the oldest, so the newest sample at or below a frame is always cached. Behaviour changes: - Several requests in flight (export transitions between two segments of one recording decode both through the same decoder) each get their own frame. The highest used to be the target, and a lower one far from it got the target's frame. - A request behind the decoder with nothing cached at or below it seeks back. Within half the cache size it used to get the next decoded, later frame. - Every request slides the readahead, cache hits included, so a sequential reader no longer drains the cache while the decoder idles. - Readahead stops at its frame count, or before the next sample would evict the one answering the latest request (frames too big for the cache budget used to get a later frame); the per-batch time budget is gone. - Samples outside the old cache window are cached too, so a frame after a VFR gap past the window gets its own sample, not the one before the gap. - A failed seek answers what waits from the cache instead of reading on from the old position as if it had landed. Export output is byte-identical to the previous decoder on the reference recording.
The decoder thread copied each sample to a staging texture and mapped it straight away, so it sat blocked in Map while the GPU decoded and copied, and the GPU sat idle while the CPU copied the frame out. read_nv12 now queues the copy of the next sample before mapping the current one, through a ring of two staging textures created with the decoder. Samples are copied from Media Foundation's output surface directly into staging, skipping the pool texture that only the disabled zero-copy path needs. The rendering decoder caches the CPU NV12 data itself and drops the zero-copy fields its frames could never fill, along with the two zero-copy constructors that were their only producers.
… job An export that ffprobe cannot read, that has the wrong frame count, or that falls outside the requested size is now an invalid run instead of a timing sample. Benchmark processes start suspended and resume only once they are in the job object, so nothing they spawn escapes its CPU accounting, and a failed assignment kills the process. A note says why playback forces FFmpeg.
…ccluded-wake fix(gpui): don't draw a hidden window when it wakes from idle
…hile the pointer moves
… and gate the backup check to unix
…y registered images
…t-waits-for-work fix(desktop-gpui): hold the automatic update prompt until recording or export finishes
…window-frame fix(gpui): end a frame quietly when its window has already closed
…ized-studio-backup fix(recording): drop the original media backup once a plain Studio stop is published
…-install-shared-crate fix(desktop-gpui): install the CLI from Settings with the shared cli-install crate
… restarting the display link
…erprint perf(recording): compare the cursor bitmap before encoding its TIFF while the pointer moves
…single-frames perf(gpui): draw one frame per wake while a window is idle instead of restarting the display link
… message on status change
…, and surface unmapped rows
…cierge-migration-20260921 feat: add Cap Pro Loom concierge migration
…rs instead of React CapSoftware#441
4 of 6 tasks
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.
Summary
flatten_rgba_to_rgb_over_white(the opaque share JPEG path inencode_jpeg, quality 90) reserved capacity and thenextend_from_slice'd a 3-byte array for every pixel. It now allocatesvec![0u8; rgba.len() / 4 * 3]and writes each pixel intodst[i..i + 3]in the same three branches, with the same integer blend ((channel * a + 255 * inv + 127) / 255).One loop is kept.
rgba_to_rgbandencode_jpeg_rgba_as_rgbare unchanged (CapSoftware#2672).Output is byte-identical to the previous formula:
alpha == 255) copies RGBalpha == 0) writes whiteThis pull request is on
kyzer1023/Cap. A pull request againstCapSoftware/Cap:mainwas not opened: this workspace's source of truth is the fork. Forkmainwas fast-forwarded to CapSoftware/Capmain(85219015) so the diff is only this commit.Profile
apps/desktop-gpuiis its own Cargo workspace, so it does not inherit the root workspace[profile.release] opt-level = "s". The profile the GPUI app ships with is:opt-levelis unset, so Cargo's release default applies:opt-level = 3. Confirmed on the bench binary:-C opt-level=3 -C lto=thin -C codegen-units=1 -C strip=symbols.Benchmarks
kernels/benches/frame_export.rsinlines the previousextend_from_slicefunction as the before case (flatten-rgb-*-4k). Equality is asserted on the 4K buffers before timing.rustc 1.99.1 (stable). Each run: 5 warmup iterations, median of 25 samples, order alternated. The table is the median of 5 such runs. 4K is 3840×2160.
Opaque and fully transparent frames are faster. Mixed alpha is about 2% slower, inside the 3% band, so the write loop was left as the presized
dst[i..i + 3]stores. An earlier indicative figure of ~2.2× on stable for 4K opaque was measured atopt-level = 3as well and did not reproduce on this VM.Tests
cap-gpui-kernelslib tests: 42 passed, 0 failed, 1 ignored (benchmark_waveform_peaks) on both rustc 1.88.0 and 1.99.1.flatten_rgba_to_rgb_over_white_matches_extend_formulachecks the three alpha classes against the previous formula, plus hand-checked opaque, transparent, andalpha == 128pixels. The existing two-step comparison (flatten_onto_whitethenrgba_to_rgb) still passes.The full GPUI app was not built.
[patch."https://github.com/wingleeio/zed"]points atzed-cap, which is not in this VM, soapps/desktop-gpui/kernelswas extracted into a one-crate workspace with stubcap-audio/cap-projectdeps for the test and bench runs.