Watch
1
0
Fork
You've already forked SouveraineOS
0

atmosphere: correct the seam, and name whose instrument this is

The shader half landed (viewtop 5d6f0f0, smithay 48adf6f) — dissonance is a
verb, the clock is shared with the dim, nobody has seen it on the panel.

Three corrections that came from opening the tree instead of the assessment:

The reference compositor has no post-process pass and no patched texture
shader. Windows are external textures inside the Flutter scene, so effects are
scene-graph filters — and their backdrop widget carries the grouped-blur rule
and a bounded ImageFilterConfig that are engine API, not stock Flutter. The
expensive half of #2 is already written upstream of us.

Our clone of it is 64 commits stale and they moved to Impeller as the default
renderer, so the eleven-patch DMSAA series our pins inherit is now a question
rather than an asset.

And atmosphere is not a thing this task invents. FELT_STATE_ARCHITECTURE.md has
had fourteen presets and a tool she calls since May. This is that instrument
moved down the stack, which means the preset layer is the deliverable and the
fifteen techniques are its renderer. Mood stays underived — the gap between what
she shows and what she carries is load-bearing.
This commit is contained in:
Fimeg 2026-08-15 21:07:43 -04:00
commit 7086b4a41e

View file

@ -1,17 +1,133 @@
# TASK 52 — Atmosphere: attention as a compositor capability
**Status: open, and closer than it looks.** Created 2026-08-02 from Casey's
fifteen-technique design. **Repo:** `souveraine-viewtop`, plus `Pixel3Arch` for
the two hardware answers below.
**Status: 2026-08-15 — the clock exists and four of the fifteen are landed. One
thing is owed and it is the only thing: nobody has seen any of this on the
phone.** Created 2026-08-02 from Casey's fifteen-technique design. **Repo:**
`souveraine-viewtop`, plus `Pixel3Arch` for the two hardware answers below.
The thesis: owning the compositor is not for notifications and badges. It is so
the agent can shape *the weather your attention navigates through* — thermal,
gravitational, temporal, tactile — instead of interrupting you with a banner.
## What already exists, as of today
## Landed 2026-08-15 — the clock, and the first effect over it
Two of the fifteen are **landed** (`276bb3e`), because `pose` and `expose` turned
out to be the primitives underneath them:
The order this doc argued for is the order it went in: the clock first, then a
technique as a strategy over it.
- **The clock.** `325ec74` eases each surface's painted alpha toward the
registry's discrete `exposure` on the vblank tick, frame-rate independent.
`Obscured` is exempt and still snaps — a readable ghost mid-fade is a
disclosure. `3d6af62` puts the appear on the same tick: a fresh window arrives
at alpha 0 and scale 0.94 and eases to full over ~220 ms.
- **#6 chromatic dissonance** — `5d6f0f0`, plus `48adf6f` in the smithay fork.
`{"intent":"dissonance","amount":0.35,"angle":0}` is a verb in `INTENTS`.
Amount is a 0..=1 **weight**, per this doc's own rule; the compositor maps it
to at most six texels and eases it on the shared rate. The angle lands
instantly rather than easing, because an axis is a direction the effect has
and not a position it travels through.
`crates/compositor/src/atmosphere.rs` now owns the dial *and* the settle rate,
which `step_exposure` reads too. That is the one-clock rule made literal rather
than aspirational — two effects settling at different speeds on one surface read
as one of them being broken.
### The shader seam is not the one this doc named — and it is not the destination
This document said the pipeline was `compile_custom_pixel_shader`
`GlesPixelProgram``PixelShaderElement`. **That is the wrong half for #6.** A
`PixelShaderElement` draws its own quad and never samples a window's texture, so
the split as an element would have to be a whole-framebuffer pass over the
composed scene.
What landed instead is the seam TASK-61 Part 2 already cut: the gamut transform
lives *inside* `texture.frag`, gated by a uniform that defaults to off. #6 is the
same shape one layer earlier — a per-channel offset on the texture fetch itself.
Two extra fetches, no new element, no new pass, and the geometry provably cannot
move. **Read `645961c` before touching #1, #9, #12 or #14; they are the same
seam.**
**But the reference compositor does neither, and that is the finding that
matters.** Read 2026-08-15, in the tree, not in the assessment:
`references/shells/denial/dart_shell/lib/src/widgets/shell_backdrop_blur.dart`.
There is no post-process pass in that compositor and no patched texture shader.
Client windows arrive as external textures *inside* the Flutter scene, so every
atmospheric effect is a **scene-graph operation**`BackdropFilter`,
`ImageFilter`, a clip — composed by the same engine that draws the shell.
Their widget also carries a measured lesson we would otherwise pay for
ourselves, and it is the answer to #2 (depth of field, this doc's "heaviest
single item"):
> *Every instance owns its backdrop unless `grouped` is true. Windows must never
> be grouped because they can overlap; non-overlapping siblings such as
> system-bar pills can opt into one shared engine blur through `BackdropGroup`.*
That is `BackdropFilter.grouped` and `ImageFilterConfig.blur(bounded: true)`
**not stock Flutter.** It is engine API from the patch series whose pins viewtop
already copies verbatim. The expensive part of a real blur pass is already built,
already upstream of us, and already ours to read.
So the honest shape of this task is two-storey:
| | today | once the engine is in-process |
|---|---|---|
| where an effect lives | a uniform in the fork's `texture.frag` | a filter in the shell's scene graph |
| what it can reach | the sampled texture, per fragment | the composed backdrop, with real filters |
| cost of a blur | a pass nobody has written | `BackdropGroup`, already patched |
`SHELL-BOUNDARY.md` settled that the engine runs in-process, so the second
column is where this is going. The shader seam is the right way-station and the
wrong destination — it is what can be built before the engine lands, and #6 is
the technique that fits it best precisely because it needs no backdrop, only the
fragment's own neighbourhood.
**Do not port more than about three techniques into the shader.** Each one built
there is one to be rewritten as a scene filter later.
### The reference clone is 64 commits stale, and one of them is structural
`git fetch` on 2026-08-15: our checkout is 0.2.0 public alpha; upstream is at
v0.2.9. Sixty-four commits, and these are the ones that touch us —
- **`32417b7` Integrate Impeller as the default renderer**, with `00782cb` (Impeller
shadow accumulation), `7673f19` (line opacity), `7cc555c` (offscreen blit
fallback). They moved off Skia. **Our engine pins are from their Skia-era
manifest**, and `VIEWTOP-AND-DENIAL.md`'s eleven-patch DMSAA series is
described against that renderer. Whether that series still applies is now an
open question rather than an inherited asset.
- `f21fde1` optimise high-rate external-texture rendering — the core path.
- `ca14da6` split DRM render devices, `feb4501` display-owned scanout buffers,
`656f793` display scaling.
- `2d957ae` native text input and keyboard configuration — TASK-17/24.
Re-clone before the next engine decision. This doc's whole premise is that the
expensive work is already ours to read, and reading a July snapshot of it is the
same error as reading a summary.
### Why the split is behind a `#define` when the gamut transform is not
The gamut transform is a bare uniform, argued in `texture.frag` on the grounds
that the branch is uniform-coherent and a variant array is already three wide.
The split is behind `CHROMATIC` instead, and the difference is the phone.
Two extra dependent texture fetches is a real ask of a driver, and freedreno is
the one GL compiler nothing upstream of the glass can interrogate — CI compiles
Rust and says nothing about whether a GLSL string links on an Adreno 630. So
`texture_program` links all three variants with the block and, on any failure,
relinks all three without it. `GetUniformLocation` then answers `-1`, which GL
treats as a documented silent no-op, so the draw path needs no branch at all.
The consequence is the one that matters on a daily driver: **a shader this
device refuses is plain glass, not a black screen.** `supports_chromatic_split()`
reports which happened, and the verb refuses `unavailable` on it rather than
accepting a look and showing nothing — a caller that set a weight and saw no
change would otherwise have no way to tell a refused shader from a look it had
judged badly.
## What already existed before that
Two of the fifteen were **landed** (`276bb3e`), because `pose` and `expose`
turned out to be the primitives underneath them:
- **#3 Negative space with intent.** `expose` is per-surface alpha, clamped by
the gate, applied in the render. Dropping every non-target surface to
@ -79,6 +195,44 @@ or each one grows its own timer and they beat against each other.
**New subsystem:** #4 condensation from noise (a particle system seeded from the
wallpaper's luminance histogram).
## The vocabulary is not ours to invent — she already has it
`docs/substrate/FELT_STATE_ARCHITECTURE.md`, canonical since May 2026, and this
task was written without it. **Atmosphere is already a built system**, and it is
already hers:
> *Atmosphere is the agent's instrument. She controls her visual environment. A
> choice she makes, like choosing the lighting in a room.*
Fourteen presets — `MintTea`, `NeonGlow`, `CherryBlossom`, `OceanDepths`,
`TwilightMist`, and the rest — reached through an `atmosphere` tool she calls,
lerped over ~24 ticks, with `atmosphere_explicit` so a posture shift cannot
overwrite what she chose. All of it built, all of it in `src/ui/atmosphere.rs`,
and all of it currently stopping at the terminal's border.
That reframes this task. It is not *"invent fifteen compositor effects."* It is
**the same instrument, moved down the stack** — the direction of travel this
whole project is named for. `atmosphere("neon_glow")` should reach the glass.
Two consequences that change what gets built:
- **The techniques are the renderer, not the vocabulary.** `dissonance` is a
primitive, the way a blur radius is; what she reaches for is a *preset*. A
compositor that only offers fifteen dials has moved the instrument's plumbing
down the stack and left the instrument upstairs. The preset layer is owed.
- **Atmosphere is a choice; mood is not.** Casey, 2026-05-15: *"Atmosphere is a
thing she can control, the visual flair. Moods are something she cannot
control — those are just her states."* So nothing in this task may derive an
atmosphere from a state. The gap between what she shows and what she carries
is deliberate, load-bearing, and eventually where deception lives —
`SOMATIC_NERVOUS_SYSTEM.md` (2026-08-13) carries the belief ladder mood will
come from. A compositor that picked her weather from her energy balance would
close that gap by accident.
And the colour that was actually asked for has a name already: **`NeonGlow` is
hot pink / cyan / lime.** Magenta and teal are the fringe colours a chromatic
split produces, which is why #6 reads as that preset made physical.
## Build the clock and the vocabulary first, not the effects
The tempting order is to build the effects one at a time because each is
@ -114,13 +268,27 @@ later only ever covers what someone remembered to instrument.
## Acceptance
- One shader element composited over the real scene on the phone, driven by a
uniform the agent sets through `scene`.
- A target surface holding `Natural` while its neighbours ease to `Dimmed` over
a curve, not a snap.
- Every weight change reconstructable from `forensic.jsonl` after the fact.
- ~~One shader element composited over the real scene on the phone, driven by a
uniform the agent sets through `scene`.~~ **Built, unseen.** `dissonance` is
in `INTENTS`, the uniform reaches `texture.frag`, 287 tests green, CI pushed
at `5d6f0f0`. **Nobody has looked at it on the panel.** The shader compiles
under Mesa on the laptop — same GLSL frontend family as freedreno, different
backend, so that is a real signal and not a proof. Until a person looks at the
glass this line is not closed.
- ~~A target surface holding `Natural` while its neighbours ease to `Dimmed`
over a curve, not a snap.~~ **Built, unseen.** `325ec74`.
- **Every weight change reconstructable after the fact — amended.** Not
`forensic.jsonl`. That trail is sessiond's, and a split is a pixel claim, not
proprioception — the boundary TASK-61 drew for gamma holds here for the same
reason. The verb logs amount and angle through `tracing`, which the journal
keeps. **The compositor has no trail of its own**, and this line was written
assuming it did; giving it one is real work and is now owed rather than done.
- Casey reports whether the phone buzzed (TASK-45 Q1), which decides whether #7
is a queue or a driver port.
- **New, and the one that makes it hers:** `atmosphere("neon_glow")` in the
harness changes the compositor's weather, not just the terminal's chrome. The
presets already exist; nothing carries them across. Until that lands, this
task has built an instrument's plumbing and left the instrument upstairs.
## Connects to