Skip to content

perf(gpui-kernels): specialise the blit for uniform backdrops - #2656

Merged
richiemcilroy merged 2 commits into
CapSoftware:mainfrom
patrickballard:perf/screenshot-blit-backdrops
Oct 18, 2026
Merged

richiemcilroy merged 2 commits into
CapSoftware:mainfrom
patrickballard:perf/screenshot-blit-backdrops

Conversation

@patrickballard

@patrickballard patrickballard commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Scope

This measures the CPU blit in cap-gpui-kernels only. It excludes GPU rendering, PNG/JPEG encoding and upload. composite runs once per export or share on a background executor, so this is not a change users will feel as a faster app — it removes a ~10x inefficiency in the kernel itself. I'd rather state that plainly than imply more.

It does complement #2493: that made unannotated frames skip the compositor, and annotations are precisely what puts translucency into the canvas, so this speeds up the case left behind.

Why

composite clears the export canvas to zero, or fills it with white when the background is visible, before blitting the annotated frame onto it (screenshot_export.rs). The destination row is therefore wholly transparent or wholly opaque in practice, and blit_over_offset took the general float blend for both.

Source-over collapses in each case:

  • Transparent row — the destination contributes nothing, so each pixel is either the source or left untouched.
  • Opaque row — oa is exactly 1.0 for every 8-bit source alpha, so the divide drops out and the blend is the integer rounding of (s*a + d*(255-a)) / 255.

The row is classified in a single pass over the destination alpha bytes; genuinely mixed rows keep the existing float blend. The opaque branch copies whole four-pixel blocks whose source alpha is already 255, so mostly-opaque rows (annotation edges) don't regress — without that, sparse-alpha sources were 25% slower.

Output is unchanged

Not "visually identical" — bit-identical:

  • the integer form checked against the float path over all 16.6M (src, dst, alpha) combinations with an opaque destination: zero mismatches
  • the transparent-destination form over all 65k (src, alpha) combinations: zero mismatches
  • 1600 randomised blits across transparent / opaque / mixed / zero-alpha-nonzero-colour destinations with random offsets and clipping: byte-identical
  • the benchmark's own assert_eq!(before, after) passes on all 17 workloads at 4K
  • new uniform_backdrops_match_scalar_source_over covers the four-pixel block boundaries and stray-alpha rows against the scalar reference

Benchmark

cargo bench -p cap-gpui-kernels --bench screenshot_blit, medians, Apple M-series arm64, against the previous implementation:

workload backdrop before after
translucent-4k transparent 26.60 ms 2.58 ms 10.3x
translucent-4k opaque white 26.74 ms 9.04 ms 3.0x
mixed-4k transparent 25.02 ms 2.60 ms 9.6x
mixed-4k opaque white 24.99 ms 9.01 ms 2.8x
sparse-alpha-4k opaque white 3.80 ms 2.50 ms 1.5x
opaque-4k any 0.87 ms 0.87 ms unchanged
translucent / mixed varying alpha — — unchanged

No workload is slower than before.

On the added benchmark workloads

I added the transparent and opaque-white backdrops to screenshot_blit. Worth being explicit, since they're the ones this change speeds up: the existing bench only built destinations with a varying alpha ramp, which is the one backdrop composite never produces. The new rows are the two it actually does. The varying-alpha rows are kept unchanged so the general path stays covered, and it's the case where this change does nothing.

Testing

cargo test -p cap-gpui-kernels
cargo bench -p cap-gpui-kernels --bench screenshot_blit

Benchmarked and tested on macOS arm64; I haven't run it on Windows or Linux, though the kernel is platform-independent.

RetriggerView in GreptileConfidence Score: 5/5

This PR appears safe to merge; no actionable issues were found.

Summary

Speeds up blit_over_offset for rows whose destination alpha is entirely zero or entirely 255.

  • Screenshot blits take faster paths for transparent and opaque canvases.

Review was based on source inspection; tests and benchmarks were not run.

Reviews (1) · Last reviewed commit: "perf(gpui-kernels): specialise the blit ..." · Reviewed by Greptile

The screenshot export canvas is either cleared to zero or filled with
white before the annotated frame is blitted onto it, so in practice the
destination row is always wholly transparent or wholly opaque. Both let
source-over collapse:

- over a transparent row the destination contributes nothing, so each
  pixel is either the source or left untouched;
- over an opaque row `oa` is exactly 1.0 for every 8-bit source alpha,
  so the divide drops out and the blend is integer rounding of
  `(s*a + d*(255-a)) / 255`.

Classify the destination row in one pass and dispatch accordingly,
keeping the general float blend for genuinely mixed rows. The opaque
branch copies whole four-pixel blocks whose source alpha is already
255 so mostly-opaque rows do not regress.

Output is unchanged: the integer form was checked against the float
path over all 16.6M (src, dst, alpha) combinations, the transparent
form over all 65k, and 1600 randomised blits across four backdrop
kinds agree byte for byte.
@vercel

vercel Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

@patrickballard is attempting to deploy a commit to the Cap Software Inc Team on Vercel.

A member of the Team first needs to authorize it.

@richiemcilroy
richiemcilroy merged commit 6cf8477 into CapSoftware:main Oct 18, 2026
36 of 38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants