builder was a placeholder invented to keep an internal host name out of a
public tree, and nothing serves it. The workflow now declares the toolchain and
upstream's own dependency list for test_all_features, so the build contract is
in the file rather than on one machine.
The mechanism install carries the scanner's own rule definitions; a gate cannot
enter a repository it protects without its patterns appearing in that diff. The
tree is exempted by manifest, the commit is reviewed here.
The gate travels with the repository because this installation cannot read a
reusable workflow from a private repo. The pin makes drift a hard stop rather
than a silent change in what "checked" means.
Upstream checks with test_all_features and the patches live in the paths that
flag compiles, so the fork borrows that choice rather than inventing one.
Anvil, Smallvil and the cache dance stay upstream's problem.
Red sampled ahead of the fragment and blue behind it, green and alpha left
alone, so an edge fringes red and cyan while the geometry stays put. Off is
`None` and costs a scalar compare.
Behind a CHROMATIC define rather than a bare uniform like the gamut transform:
this adds two dependent fetches and freedreno is the one compiler CI cannot ask.
A driver that refuses it links the variant without the block, GetUniformLocation
answers -1, and the writes are silently dropped — plain glass instead of no
compositor. supports_chromatic_split() reports which happened, so a caller can
refuse rather than set a look and watch nothing move.
build_planes already documented this: with no exportable fence, or a surface
that cannot fence at all, the sync point has to be waited on. It never was, so
the atomic commit raced the renderer into the scan-out buffer. Tears on
simpledrm + llvmpipe, where supports_fencing is false and nothing held the flip.
The transform rides on a uniform rather than a shader variant, so an
unmanaged draw takes no colour arithmetic at all and stays byte-through.
Un-premultiplies around the sRGB transfer functions: a 3x3 commutes with
the alpha scalar, the transfer curve does not, so decoding premultiplied
values shifts hue at every alpha but 1.
Qt commits a window surface once while setting it up - the xdg-shell
habit of committing empty to solicit a configure - on a protocol that
configures on bind instead. That commit carries no buffer, so it puts no
content on a locked screen, but the check kills the client for it. On a
phone that takes the lock screen down and locks the owner out.
A commit that actually attaches a buffer before acking is still refused,
which is the case the error exists for.
This is a leniency for a client that is out of spec, not a fix for a
compositor that was. The real fix belongs in Qt.
Destroying zwlr_layer_surface_v1 and then committing the wl_surface with
a null buffer is how the protocol says to unmap a layer surface. The
pre-commit hook stays attached to the wl_surface, and at that commit the
pending LayerSurfaceCachedState no longer describes a live role, so the
size/anchor checks read defaults - width 0 with no anchors - and post
InvalidSize to the destroyed object. The client is killed for doing
exactly what it was told to do.
Seen with quickshell: it tore down its dock layer surface and was killed
mid-teardown, then crash-looped. The surface was well-formed throughout,
set_size(0, 119) with anchor bottom|left|right, configured and acked, so
the error named a defect it did not have.
Fork base: 812bd33259
`import_dmabuf` allocates an `EGLImage` with `create_image_from_dmabuf`, then
binds it to a texture with `import_egl_image`. When that bind returned an error,
the already-created `EGLImage` handle was dropped on the floor: no `GlesTexture`
ever owned it, so it was never freed, leaking the handle (and, on the proprietary
NVIDIA driver, the GPU allocation backing it). Free it directly with
`DestroyImageKHR`; the deferred-destruction queue is only needed by `Drop`
implementations that run without a current context.
Developed with AI assistance (Claude Code); all changes have been reviewed and
are understood by the author.
The `MultiRenderer` caches imported client textures per surface in
`MultiTextureInternal` (kept in the surface `data_map`), holding the
`GlesTexture`s and their `EGLImage`s. Unlike `RendererSurfaceState`, it had no
destruction cleanup, so its textures outlived the surface — until the last
`WlSurface` reference dropped — leaking client buffers in GPU memory.
Add `clear_surface_textures` and call it from the surface destruction hook in
`on_commit_buffer_handler`, mirroring `RendererSurfaceState`'s cleanup.
Developed with AI assistance (Claude Code); all changes have been reviewed and
are understood by the author.
damage_since() unions damage across commits, each rect clamped only to
the buffer dimensions of its own commit. When a client shrinks its
buffer (e.g. a cursor surface re-rendered after a fractional-scale
change), stale larger rects reach TexSubImage2D, fail the whole upload
with GL_INVALID_VALUE and leave the previous texture contents visible.
Intersect each rect with the current buffer before uploading.
Developed with AI assistance (Claude Code); all changes have been reviewed and
are understood by the author.
copy_framebuffer and copy_texture allocate a PIXEL_PACK_BUFFER, then build the
owning GlesMapping (which frees the PBO on Drop) only on the NO_ERROR branch.
On any GL error the bare buffer handle was dropped, leaking a region-sized GPU
buffer on every failed readback. Free the buffer on the error branches, and
compute bpp before allocating it so an early return cannot leak it either.
Developed with AI assistance (Claude Code); all changes have been reviewed and
are understood by the author.
`Slot::export` caches the exported `Dmabuf` in the slot's `UserDataMap` via the
non-thread-safe `insert_if_missing`, which stamps it with the creating thread's
id. `UserData::drop` runs the value's destructor only on that same thread and
otherwise deliberately leaks it (to avoid dropping possibly-`!Send` data on the
wrong thread).
A slot first exported on one thread but dropped on another therefore leaks the
cached `Dmabuf`: its `Arc` strong count is never decremented and the underlying
GBM buffer stays pinned for the process lifetime. In a multi-threaded compositor
this shows up as GPU memory growing steadily across output reconfigures.
`Dmabuf` is `Send + Sync`, so cache it with `insert_if_missing_threadsafe`. The
`GbmFramebuffer` caches in `GbmBufferedSurface` have the same defect (their
`Drop` calls `destroy_framebuffer`) and are fixed the same way.
Developed with AI assistance (Claude Code); all changes have been reviewed and
are understood by the author.
`SimpleCrtcMapper` keeps a `connector -> crtc` reservation map and, on each `map()` call, releases the reservation of any connector it is told about that is no longer connected:
```rust
for connector in connectors
.clone()
.filter(|conn| conn.state() != connector::State::Connected)
{
self.crtcs.remove(&connector.handle());
}
```
This only releases connectors that are still present in the resource list and reported as disconnected. A connector whose handle disappears from the resource list entirely is never passed to `map()` again, so its reservation is never released and leaks for the lifetime of the mapper.
This happens routinely with DP-MST: the sink connectors behind an MST hub are dynamically created and destroyed, so unplugging a USB-C/Thunderbolt dock — or suspending and resuming while docked, where the connectors are torn down and recreated under fresh handles while the compositor isn't scanning — makes the old handles vanish rather than transition to `Disconnected`. Each such event leaks one reservation per vanished connector. Because a leaked reservation still counts as "taken", the reservations accumulate across cycles until every CRTC is reserved by a ghost handle. At that point `pick_next_available_for_connector` can no longer find a free CRTC for a genuinely connected connector, so `crtc_for_connector` returns `None` for it and the compositor silently never brings up that output. The only recovery is recreating the `DrmScanner`.
This was observed in niri as external monitors on a dock going missing after a number of suspend/resume cycles, getting progressively more likely over time, and only recoverable by restarting the compositor.
Fix it by releasing the reservation of every connector that is not currently connected, including connectors that are no longer present at all. At the start of `map()`, collect the set of currently connected connector handles and retain only reservations whose handle is in that set. The previously handled case (a connector present but reported as disconnected) is still covered, since such a connector is excluded from the connected set.
use case:
application can advertise to the wayland client that it supports
these formats. and can create shaders to convert dmabufs or
shared memory buffers into RGBA formats
Signed-off-by: Hichem, Ben Fekih <hichem.f@live.de>
`zwp_linux_buffer_params_v1` specifies that planes should start at 0 and
be consecutive (but can be added out of order). Add a check for this,
and send a protocol error if it fails.