Repository navigation
feat(desktop-gpui): Cap Glass, a cross-platform glass material - #3442
richiemcilroy wants to merge 24 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…indow by area on X11
…and its edge hairline
…e grey axis to Apple's
|
hey @greptileai, please re-review the PR |
| - "apps/desktop-gpui/src/ui/glass.rs" | ||
| - "apps/desktop-gpui/src/glass_probe.rs" | ||
| - "scripts/glass-probe/**" | ||
| - ".github/workflows/glass-probe.yml" |
There was a problem hiding this comment.
P2: Fork PRs can queue the full 3-OS, 75-minute glass probe by editing only the workflow file
Workflow lists its own file in pull_request paths and gates a 3-OS, 75-minute build/probe matrix, so a fork can queue it by editing only CI.
Add the same-repo scope job used by perf-budget.yml/instant-reliability.yml, or drop the workflow's own path from the trigger.
AI prompt
Check if this security scanner issue is valid. If so, understand the root cause and fix it. If appropriate, update or add tests. Keep the change focused and preserve intended behavior.
<file name=".github/workflows/glass-probe.yml">
<violation number="1" location=".github/workflows/glass-probe.yml:16">
<priority>P2</priority>
<title>Fork PRs can queue the full 3-OS, 75-minute glass probe by editing only the workflow file</title>
<evidence>The newly added workflow runs on `pull_request` (`opened, synchronize, reopened, ready_for_review`, line 8-9) and lists its own file among the path filters at line 16 (`- ".github/workflows/glass-probe.yml"`). The job it gates has `timeout-minutes: 75` (line 29) and a matrix of three runners (lines 32-45): `blacksmith-6vcpu-macos-15`, `windows-2022`, and `blacksmith-8vcpu-ubuntu-2404`. Each matrix leg checks out the PR, runs `node scripts/setup.js` and `./scripts/prepare-gpui-dependency.sh`, builds the complete `cap-gpui` binary, runs `cargo test -p cap-glass`, rewrites the macOS TCC database with sudo, and launches the real app to render/capture 9 scenes x 4 presets. A fork contributor can therefore trigger all of that work with a commit that touches only `.github/workflows/glass-probe.yml`, with no product change. The repository already fixed this exact self-trigger pattern with same-repo scope jobs in `perf-budget.yml` (scope job, base lines ~26-69, comment "a fork cannot queue the macOS job by editing these files") and `instant-reliability.yml` (scope job, base lines ~41-70); glass-probe.yml copies the older ungated picker-smoke shape instead.</evidence>
<recommendation>Add a lightweight `scope` job like `perf-budget.yml`/`instant-reliability.yml` that computes the changed files, and only set `run=true` for a workflow/script-only change when `github.event.pull_request.head.repo.full_name == github.repository` (keep `workflow_dispatch` and same-repo PRs unrestricted). Alternatively, remove `.github/workflows/glass-probe.yml` from the paths filter and validate CI-only changes with a bounded job. Keep the full 3-OS probe for changes to actual glass code.</recommendation>
</violation>
</file>
| + // nothing else is painted above it. The extra 2px covers glass's | ||
| + // hairline. | ||
| + let layer_bounds = bounds.scale(scale_factor).dilate(ScaledPixels(2.)); | ||
| + self.next_frame.scene.push_layer(layer_bounds); |
There was a problem hiding this comment.
Shadow batching can suppress a glass pane on Metal
Medium Severity
The existing Metal renderer processes pending blurs only before a batch whose first draw order reaches the blur, and does not flush remaining blurs afterward. A shadowed glass::pane with no painted children emits its real drop shadow before this layer, followed by the invisible splitter shadow inside it. Scene::BatchIterator merges those shadows into one batch starting below the glass order, so Metal never draws the pane. Giving the splitter and glass the same order does not guarantee a batch boundary. Split batches at backdrop orders explicitly; the new DirectX and wgpu scheduling also needs that boundary to avoid drawing later shadows before an intervening glass pass.
| let mut out = backdrop.clone(); | ||
| for shape in shapes { | ||
| let style = material.resolve(shape.width, shape.height); | ||
| composite(&style, shape, &backdrop.blurred(style.blur), &mut out); |
There was a problem hiding this comment.
Overlapping panes miss earlier glass
model::render blurs the original backdrop for every pane. When panes overlap, a later pane ignores the glass already drawn beneath it. The GPU instead snapshots the window before each pane, so the CPU reference produces different colours and blur in the overlap.
Blur the current out before drawing each pane, and add an overlapping-pane test.
| composite(&style, shape, &backdrop.blurred(style.blur), &mut out); | |
| composite(&style, shape, &out.blurred(style.blur), &mut out); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop-gpui/glass/src/model.rs
Line: 278
Comment:
**Overlapping panes miss earlier glass**
`model::render` blurs the original `backdrop` for every pane. When panes overlap, a later pane ignores the glass already drawn beneath it. The GPU instead snapshots the window before each pane, so the CPU reference produces different colours and blur in the overlap.
Blur the current `out` before drawing each pane, and add an overlapping-pane test.
```suggestion
composite(&style, shape, &out.blurred(style.blur), &mut out);
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.|
hey @greptileai, please re-review the PR |
| LogicalPosition::new( | ||
| f64::from(f32::from(bounds.origin.x)) - origin.x(), | ||
| f64::from(f32::from(bounds.origin.y)) - origin.y(), | ||
| ), | ||
| LogicalSize::new( | ||
| f64::from(f32::from(bounds.size.width)), | ||
| f64::from(f32::from(bounds.size.height)), |
There was a problem hiding this comment.
On X11 with a backing scale above 1, window_target passes logical window coordinates to a capture path that reads physical pixels. At scale 2, it captures roughly 960×600 pixels while the expected image is 1920×1200. crop_to cannot enlarge the capture, so metrics::ssim panics before the probe writes its report.
Apply the X11 scale conversion already used by capture_scale_factor when building these capture bounds.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop-gpui/src/glass_probe.rs
Line: 257-263
Comment:
**Scaled X11 probe fails**
On X11 with a backing scale above 1, `window_target` passes logical window coordinates to a capture path that reads physical pixels. At scale 2, it captures roughly 960×600 pixels while the expected image is 1920×1200. `crop_to` cannot enlarge the capture, so `metrics::ssim` panics before the probe writes its report.
Apply the X11 scale conversion already used by `capture_scale_factor` when building these capture bounds.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.…, colour and tint
Stacked on #3454. Part of the picker stack: #3441, #3450, #3452, #3454, this, #3456.
What this adds
Cap Glass is a glass material for the GPUI app. It renders the same on macOS, Windows and Linux, and was fitted against Apple's
NSGlassEffectView.Renderer (
apps/desktop-gpui/patches/zed-glass.patch)The gpui fork's
BackdropBlurexisted only in the Metal renderer. This patch extends it with aGlassStyleand implements it in all three renderers: Metal (shaders.metal), DirectX 11 (shaders.hlsl) and wgpu (shaders.wgsl).Per pane, the renderer:
Window::paint_glasswraps the pass in its own scene layer, so the glass draws even when nothing else is painted above it.prepare-gpui-dependency.shapplies the patch like the others.Package (
apps/desktop-gpui/glass, cratecap-glass)model.rsis a CPU model of the same material. It is the reference the GPU output is checked against.presets.jsonholds the four fitted materials: Regular and Clear, each in light and dark.glass-fitfits a material to reference captures, at whatever backing scale they were taken. The colour transform is linear in its coefficients, so each evaluation solves it exactly by weighted least squares (neutral scenes weighted 12x). Nelder-Mead searches only blur, lensing and lighting.src/ui/glass.rsis the app API:glass::pane(kind, dark, radius)gives aDivwhose first child is the glass, so content paints on it.Reference captures (
scripts/glass-reference/reference.swift)The script draws 13 scenes (flat greys, black, white, text, dense text, checker, grid, stripes, dots, saturated blobs, hues, a UV ramp, a split field). It puts
NSGlassEffectViewpanes over them in each style and appearance, at several sizes and radii, and captures them.The references were captured on a MacBook Pro (M4 Max, macOS 27). CI VMs render Liquid Glass in a reduced form, so references must come from real hardware. To regenerate them, run the script on a Mac with Screen Recording access, then
glass-fit <dir> <out>.Fit against Apple
Per preset, over the 13 scenes, inside the glass:
Notes on the table:
tests/presets.rspins this.GPU against the model, on every OS
The glass probe (
CAP_GLASS_PROBE_OUTPUT,.github/workflows/glass-probe.yml) runs on all three OSes:On Windows and Linux it captures the window's screen area. Hosted Windows runners have no window capture, and Cap's own windows aren't in the X11 window list.
It passes at SSIM ≥ 0.97 and mean ΔE ≤ 2.
Cost
cap-glassbuilds atopt-level = 3in dev, so the probe and the fitter run at full speed.Testing
cargo test -p cap-glassruns the preset calibration against Apple's flat values (wired intoci.yml).node --test scripts/prepare-gpui-dependency.test.mjscovers the new patch's place in the patch list.

Confidence Score: 5/5
The PR appears safe to merge, with non-blocking gaps in the CPU reference and the scaled X11 probe.Findings
Fix with agent prompt
Summary
Adds Cap Glass rendering for Metal, DirectX 11, and wgpu, plus fitted presets and a shared CPU model.
Reviews (6) · Last reviewed commit: "Merge feat/gpui-window-arrange-focus int..." · Reviewed by Greptile