Skip to content

windows: a composition spike for the overlay's four questions - #241

Merged
foxnne merged 1 commit into
mainfrom
windows/composition-spike
Oct 8, 2026
Merged

foxnne merged 1 commit into
mainfrom
windows/composition-spike

Conversation

@foxnne

@foxnne foxnne commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

Part of #226 (docs/NATIVE_WINDOWS_PLAN.md, Phase 2); step 5 of docs/WINDOWS_LINUX_GLASS_PLAN.md (#236).

What changes

Adds spikes/windows-composition/, a standalone Windows program answering the four questions the plan's Phase 2 (the drag's glass as an overlay over the desktop's blur) waits on. Nothing in fizzy changes. The research doc's step 5 records the answers.

How it works:

  • It uses fizzy's own pins of fizzyedit/SDL and zigwin32.
  • The window is fizzy's kind: a transparent SDL window presenting on D3D12 through the fork's topmost DirectComposition target.
  • Under it: a Windows.UI.Composition target that isn't topmost, holding the host backdrop, cut to a pill (∪ an orbiting circle) that sweeps across the window. Five modes:
    • no clip;
    • a rounded-rect clip;
    • a Direct2D path rebuilt every frame;
    • an AlphaMask effect over a swapchain the app presents;
    • the app's picture hosted in composition's own tree (the fallback for question 1).
  • An overlay over the whole display tries four click-through styles.
  • WinRT is bound by hand. Every GUID and vtable slot was read from the OS's own WinMetadata with System.Reflection.Metadata, and each is cited in src/winrt.zig. IGeometrySource2D and IGraphicsEffect are COM objects written in Zig.

At about 1,700 lines this is over CONTRIBUTING's 800-line guide. It's throwaway code in spikes/, built by nothing else.

SDK impact

  • None

Verified

Windows 11 ARM VM (fizzy-win11, build 26300, D3D12 on WARP, 60 Hz), aarch64-windows-gnu, driven by SendInput. Offsets were measured from screenshots: the clip's glass against the red box SDL presents round the pill, six samples per run, with a direction mark telling lead from lag.

  1. Two targets on one HWND: yes. CreateDesktopWindowTarget(hwnd, FALSE) on SDL's claimed window succeeds and draws under SDL's picture. Static, the two align to the pixel.

  2. Path clip rebuilt every frame: works and is antialiased (1–2 px). Composition calls GetGeometry once per frame. With SDL's VSYNC present the clip leads the picture by the present queue:

    SDL present Clip delay Glass vs SDL's rim
    VSYNC (frames in flight 1 or 2) 0 ~2.2 frames ahead (35 ms)
    VSYNC 1 1.3 frames ahead
    VSYNC 2 0 ±0.5 px
    MAILBOX / IMMEDIATE 0 0 ±0.5 px

    So fizzy either gives composition the shape from as many frames back as the present queue is deep (measured, not assumed), or has the fork present through a frame-latency waitable swapchain.

  3. Effect-graph mask: yes. D2D's AlphaMask with a CompositionEffectSourceParameter over CreateCompositionSurfaceForSwapChain shows the glass exactly where the presented mask is.

  4. Overlay:

    Style Same-thread click Another process Tree shows
    TRANSPARENT | LAYERED passes passes yes
    TRANSPARENT | LAYERED, alpha 255 passes passes yes
    TRANSPARENT alone blocked blocked yes
    HTTRANSPARENT passes blocked (though WindowFromPoint says it passes) yes

    A drag under the layered overlay keeps its capture (30/30 motion events). SDL_SetWindowOpacity(0.6) (layered) leaves both SDL's picture and the composition tree drawing.

  • The blur on a hardware GPU. The VM draws the host backdrop black. Run it, press 3 (path) with the backdrop on, and check the blur and the edge, at clip delay 0 and 2 (D).
  • The present queue's depth at 60/120/144 Hz on hardware, and Windows 11 23H2/24H2.
  • macOS / Linux / Web: n/a (Windows only; not part of fizzy's build).

Follow-ups

  • If the blur holds on hardware: the overlay (step 6), with the clip delayed by the measured present-queue depth, or a waitable swapchain in the SDL fork.

🤖 Generated with Claude Code

`spikes/windows-composition/`: a Windows program on fizzy's own pins of fizzyedit/SDL and zigwin32
that answers step 5 of docs/WINDOWS_LINUX_GLASS_PLAN.md, the spikes NATIVE_WINDOWS_PLAN.md's
Phase 2 waits on. A transparent SDL window presents on D3D12 through the fork's topmost
DirectComposition target, as fizzy's windows do. Under it, a Windows.UI.Composition target that
isn't topmost holds the host backdrop, cut to a moving pill (∪ an orbiting circle) by a rounded-rect
clip, a Direct2D path rebuilt each frame, or an AlphaMask effect over a swapchain the app presents.
A display-sized overlay tests click-through styles.

WinRT is bound by hand. Every GUID and vtable slot was read from the OS's own WinMetadata with
System.Reflection.Metadata, and `IGeometrySource2D` and `IGraphicsEffect` are implemented as COM
objects in Zig.

Run in the Windows 11 ARM VM (WARP, 60 Hz) with real input, offsets measured from screenshots:
1. The two targets share the HWND, aligned to the pixel.
2. The path clip is antialiased. It leads SDL's VSYNC present by the present queue (2 frames, 35 ms
   at 60 Hz), and lands exactly (±0.5 px) with a 2-frame clip delay or with a present that doesn't
   wait. Frames in flight make no difference.
3. AlphaMask takes a presented swapchain as its mask.
4. Only `WS_EX_TRANSPARENT | WS_EX_LAYERED` passes clicks to other processes while the tree still
   shows. `HTTRANSPARENT` passes only within the thread, though `WindowFromPoint` says otherwise.
   A drag under the overlay keeps its capture, and `SDL_SetWindowOpacity` (layered) leaves both
   trees drawing.

The VM draws the host backdrop black, so the blur and real-refresh timings need a hardware GPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne force-pushed the windows/composition-spike branch from 6be5d0f to ed8b892 Compare October 8, 2026 18:01
@foxnne
foxnne marked this pull request as ready for review October 8, 2026 18:02
@foxnne
foxnne merged commit 3a2a4f8 into main Oct 8, 2026
7 checks passed
@foxnne
foxnne deleted the windows/composition-spike branch October 8, 2026 18:19
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.

1 participant