Background blur for layer surfaces and translucent windows - #3
Conversation
4837467 to
f9f485a
Compare
|
Blur cost, measured. FLX1s (720x1600), six runs, two per configuration. phoc, % of one core:
Zero at idle, roughly double during animation, peak near half a core. phosh unaffected. Repeat runs of the same configuration reproduce phoc to 0.3-6% on drawer and app grid, so the doubling sits well outside noise. Governor pinned to performance, background package activity killed, every run started from the same temperature. Caveats: the FLX1 has 2.3x the pixels and is not yet measured, and this is CPU only. The kawase pass is GPU work and was not measured. |
f9f485a to
6bde733
Compare
The blur request needs somewhere to live. Adding it to zphoc_layer_shell_effects_v1 would mean editing a protocol phoc owns: our version 4 and any version 4 phoc later defines would be two different things under one name, and a client could not tell them apart. So define zphoc_furios_layer_shell_effects_v1 instead, in a file of our own, with get_blur_layer_surface and the zphoc_furios_blur_layer_surface_v1 it hands back. phoc's protocol is left exactly as it is. The radius is documented as 0 to 128, with neighbouring values allowed to look the same: the compositor quantises it into pyramid levels, and above 128 every value is the same picture. Signed-off-by: wayne <wayne@furilabs.com>
Signed-off-by: wayne <wayne@furilabs.com>
Mirrors PhocAlphaLayerSurface: same lifecycle, same listeners, pending radius applied on commit. Changing the radius damages the whole output, since blurred pixels do not follow the surface's own damage. The request arrives on our own global rather than phoc's, so this binds zphoc_furios_layer_shell_effects_v1 alongside the existing one and leaves zphoc_layer_shell_effects_v1 at version 3, exactly as phoc defines it. Signed-off-by: wayne <wayne@furilabs.com>
A dual-Kawase pyramid over plain GL textures and framebuffers. The source is the output's own framebuffer, copied with glCopyTexSubImage2D from inside the render pass. That shape is forced by the hardware rather than chosen: on the android renderer a wlr_allocator buffer cannot back a render target -- no dmabuf import, no native window -- and an earlier revision that rendered into one produced incomplete framebuffers and aborted the compositor at startup. phoc says as much itself, in the "Do not use wlr_allocator on android" path of phoc_renderer_render_view_to_buffer(). The pyramid halves resolution per level rather than widening the tap offset, because past roughly a texel of separation the samples stop overlapping and read as discrete copies -- on diagonal taps, a visible star pattern. Application windows are blurred without asking, decided from their opaque region, since a toplevel has no way to request it over the layer shell effects protocol. The test is "claims no opaque pixels at all" rather than "does not cover itself": a window with rounded corners reports an opaque region a few pixels short of its own extent while being opaque everywhere it paints, and the weaker test would put a blur pass behind it that nothing can see. The output damages itself whole while a blurred surface is up, and copies the whole framebuffer, and both are deliberate. The pyramid's coarse levels are a sixty-fourth of the output, so one of their texels is sixty-four pixels wide and a blurred pixel is a weighted sum of content from hundreds of pixels away, recomputed over the whole capture every frame. Repainting only around the damage was tried on device: the patch is computed from a capture whose other parts were taken at a different time, so it does not match what surrounds it -- visibly, as a block of different blur behind each key of the lock screen keypad -- and the framebuffer outside the damage still holds the previous frame's composite, blurred surface included, which the pyramid then feeds back into itself. Neither is fixable by growing a margin: a pyramid's footprint is not local. What that costs is bounded by asking first, in phoc_output_draw(): nothing is promoted unless a surface has asked for blur and the renderer can draw it. An idle screen damages nothing and renders nothing at all. The radius is scanned once a frame and handed to the renderer in the render context, rather than rediscovered for every surface drawn. Nothing is blurred unless a surface asks for it, so that request is the opt in. Signed-off-by: wayne <wayne@furilabs.com>
Blur is on by default and a surface only gets it by asking over the layer shell effects protocol, but a device whose GL path misbehaves needs a way to switch the effect off without downgrading the compositor. Read at startup and followed while phoc runs; the backdrop is captured from the render pass, so nothing frosts or clears until a frame is asked for. Signed-off-by: wayne <wayne@furilabs.com>
Covers the request and that a blurred surface still renders correctly. The GL path itself only runs on a device: the suite forces WLR_RENDERER=pixman and no gles2 renderer can be created under Xvfb, so what runs here is the protocol plumbing and the fallback when blur is unavailable. Signed-off-by: wayne <wayne@furilabs.com>
6bde733 to
003cf8c
Compare
|
Re-measured after the performance pass, on an FLX1 this time (krypton, phoc, % of one core:
GPU load, which the earlier run had no reading for — MediaTek's phosh is unchanged within noise: 27.1 vs 26.9 on app open, and its drawer Idle costs nothing at all, which is the design: an idle screen damages Drawer is the number to trust — 1.90x, with the repeats inside 6% on both App grid is not separable and I am not quoting a figure for it. phoc and App open is directionally solid but noisy — every glass-on run sits above The whole-output damage that these numbers measure is unchanged from the table |
Background blur for layer surfaces that ask for it, and for translucent
application windows.
layer-shell-effects: add a background blur request— bumpszphoc_layer_shell_effects_v1to version 4 and addsget_blur_layer_surface. The radius is double buffered and takes effect onwl_surface.commit, following the alpha surface it is modelled on.layer-surface: track a background blur radiuslayer-shell-effects: implement the blur layer surface— mirrorsPhocAlphaLayerSurface: same lifecycle, same listeners.renderer: blur the backdrop of translucent surfacestests: exercise the blur layer surfaceThe renderer commit is the large one and does not split further — the filter,
the capture and the two call sites are one mechanism. Its shape is forced by the
hardware rather than chosen: on the android renderer a
wlr_allocatorbuffercannot back a render target (no dmabuf import, no native window), and an earlier
revision that rendered into one produced incomplete framebuffers and aborted the
compositor at startup. phoc says as much itself, in the "Do not use
wlr_allocator on android" path of
phoc_renderer_render_view_to_buffer(). Sothe dual-Kawase pyramid runs on plain GL textures and FBOs, with the source
copied out of the output's own framebuffer with
glCopyTexSubImage2Dfrominside the render pass. The pyramid halves resolution per level rather than
widening the tap offset: past roughly a texel of separation the samples stop
overlapping and read as discrete copies — a visible star pattern on diagonal
taps.
Application windows are blurred without asking, decided from their opaque
region, since a toplevel has no way to request it over the layer shell effects
protocol.
Nothing is blurred unless a surface asks for it, so that request is the opt-in.
On top of that the
blurgsetting switches the whole effect off, for a devicewhose GL path misbehaves, without downgrading the compositor. It defaults to
on, is read at startup and followed while phoc runs -- the same shape as the
touch-pointskey in thescreen-capturePR, damaging the outputs on changebecause the backdrop is captured from the render pass. An earlier revision of
this branch used a
PHOC_BLURenvironment variable for that; it has beenreplaced by the setting at review request, and is gone.
On testing: the GL path only runs on a device. The suite forces
WLR_RENDERER=pixmanand no gles2 renderer can be created under Xvfb, so whatthe new test covers is the protocol plumbing and the fallback when blur is
unavailable. The renderer itself has been exercised on an FLX1 (Halium/hybris,
Mali-G68) across many boots, including rotation.
How it reaches the GPU
Nothing is read back to the CPU anywhere in this path -- there is no
glReadPixelsand no buffer mapping.phoc_renderer_capture_blur()runs inside the output's existingwlr_render_pass, called fromrender_layer()just before a surface thatasked for blur is drawn. It does not begin a render pass of its own, and that
is deliberate: the android renderer has no dmabuf import and no native window
for an offscreen
wlr_allocatorbuffer, so a second render target is notavailable here --
phoc_renderer_render_view_to_buffer()already says as much.glCopyTexSubImage2Dcopies the output framebuffer, as itstands at that moment, into a GL texture. Driver-side blit.
up-sample shaders drawing a 4-vertex triangle strip. The pyramid starts at
quarter resolution, cutting the fill rate of every iteration by 16.
Depth follows the radius:
levels = round(log2(max(radius, 8) / 8)), clampedto 0..4, so radius 8 is one level and each doubling buys one more.
render_blur_backdrop()draws the result as a raw GL quadunder the surface.
PhocBlurStateper output (capture texture, five pyramidtextures and their FBOs) in a hashtable on
PhocRenderer, dropped byphoc_renderer_forget_output(). Allocated once per output, not per frame.The FBO binding and surrounding GL state are saved and restored, since this
borrows wlroots' render pass rather than owning it.
Cost when idle is nil, because
phoc_output_draw()renders no frame at allwhen nothing is damaged. The cost that is real: when blur is active and the
damage ring is non-empty the whole output is damaged, since a blurred pixel
depends on hundreds of pixels around it and partial redraw artifacts. So
anything that damages a small region every frame repaints the whole output.
That promotion is now asked for twice before it happens — a surface must want
blur and the renderer must be able to draw it — so with
blur falsenothingis promoted.
gsettings set sm.puri.phoc blur falsegives a live A/B on thesame device.
Pairs with FuriLabs/phosh#4, which requests the blur. phosh binds this
protocol only if the compositor offers it and is deliberately outside its
"lacks needed globals" fatal check, so it still starts against a stock phoc and
simply gets no blur. The two can land in either order.
6 commits, off
forky.Updated at review request. Two comment additions that had been appended to
existing lines now sit on their own lines, so those lines are untouched again;
and
tests/testlib.cis back to upstream'sDISPLAY/WLR_BACKENDShandling(
tests/testlib.cis +5/-0, just the FuriOS global's bind and cleanup). The onlyupstream hunk this branch still changes is in
src/output.c, where the damageregion has to be computed before the render pass so the backdrop capture can
sample it — 3 lines, a move rather than a rewrite.
17/17 tests pass under
xvfb-run.Updated after the performance review
The whole-output damage is real and stays. Narrowing it was implemented and
tested on device, and withdrawn: the patch is computed from a capture whose
other parts were taken at a different time, so it does not match what surrounds
it — visibly, as a block of different blur behind each key of the lock screen
keypad — and the framebuffer outside the damage still holds the previous
frame's composite, blurred surface included, which the pyramid then feeds back
into itself. Growing a margin does not fix it: the pyramid's coarse levels are
a sixty-fourth of the output, so one of their texels is sixty-four pixels wide.
A pyramid's footprint is not local.
The promotion never consulted the renderer, and that was a defect rather than
a slow path. Both drawing paths check
phoc_renderer_blur_enabled; the damagetest did not. With
sm.puri.phoc blur falseevery damaged frame was stillpromoted to a full repaint and no blur was drawn. Fixed.
Also from that pass, all behaviour-preserving:
render context, rather than rediscovered for every surface drawn.
phoc_output_get_blur_radius()is gone, and with it theoutput.hchange —this branch no longer touches that header at all.
phoc_view_wants_blur()moved tosrc/view.c, beside the view code itreasons about.
levels = round(log2(max(radius, 8) / 8))clamps to 4, so the filtersaturates at 128. The XML now says 0 to 128, that values above are
clamped, and that the compositor may quantise within the range so two
neighbouring values can produce the same result. phosh#4's
glass-blur-radiusrange was 0-200 and is corrected to match.
Re-measured after this pass, on an FLX1 this time — see the comment below.
The first table in this thread was taken on an FLX1s before the pass and is
superseded by it. Short version: nothing at idle, and roughly double during
animation, which is the whole-output damage above.