chromium-fourier r2 + firefox-fourier 150.0.1 + KWIN_PIVOT.md

chromium-fourier:
- patch 3/3 nv12-external-oes-on-modifier-external-only.patch — adds
  NativePixmapEGLBinding::ModifierRequiresExternalOES helper, extends
  OzoneImageGLTexturesHolder::GetBinding to honor EGL external_only
  flag for NV12 dmabufs on panfrost / panthor. Validated on ohm
  (RK3566 hantro mainline 6.19.10): bbb_1080p30_h264.mp4 plays at
  34.7 % combined CPU vs ~131 % pre-patch baseline (~3.8x).
- PKGBUILD pkgrel 1->2, source array + sha256sums + prepare() hook for
  patch 4, patch numbering 1/2,2/2 -> 1/3,2/3,3/3.
- NEXT.md appended with 2026-04-28 section: patch 4 design, validation
  log, KWin GL_ALPHA bug pinpoint (preexisting since 2026-03-06,
  affects every wayland video client; unrelated to chromium-fourier),
  device-renumbering note (/dev/video1 = encoder post-reboot).
- KWIN_PIVOT.md: 4-phase plan to identify and patch KWin's
  glTexImage2D(internalFormat=GL_ALPHA) site, ohm-only test plan,
  scope discipline.
- patches/ now tracked (compiler-rt-adjust-paths, enable-v4l2,
  wayland-allow-direct-egl-gles2, nv12-external-oes); the dead-end
  chromeos-pipeline-bypass.patch removed.

firefox-fourier:
- 4 patches (gfxinfo v4l2 stateless fourccs, libwrapper hwdevice ctx,
  ffmpegvideo v4l2-request route, prefs v4l2-request default).
- PKGBUILD bumped to firefox 150.0.1, Arch toolchain glue patches
  layered in, mozconfig with --without-wasm-sandboxed-libraries for
  ALARM, package() launcher fix (rm -f symlink before cat > to avoid
  ENOENT through the dangling /usr/local symlink mach install drops).
- 150.0.1-1-aarch64.pkg.tar.zst built on boltzmann (95 MB), pending
  fresnel power-on for V4L2 stateless validation on RK3399.
This commit is contained in:
2026-04-28 12:02:18 +00:00
parent 7bb2fbeca9
commit 8756ce38be
15 changed files with 1711 additions and 60 deletions
+278
View File
@@ -148,3 +148,281 @@ to install chromium's bundled clang (x86_64 host, arm64 sysroot), then
The boltzmann `chromium-builder` LXD container is preserved as fallback
but no longer the active build host. If cross-compile pans out, that
container can be torn down.
## First runtime validation on ohm — 2026-04-26 22:26 UTC
Cross-compile produced a working aarch64 binary (chrome 647 MB ELF +
chrome_crashpad_handler 4.3 MB + .pak + locales). Tarball
`chromium-fourier-147.tar.gz` (226 MB) transferred CT 220 → hertz → ohm.
Launched in mfritsche's KWin Wayland session (tty2, panfrost render
node) playing `bbb_1080p30_h264.mp4` from file:// with
`LIBVA_DRIVER_NAME=v4l2_request`,
`LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video0`,
`--use-gl=egl --ozone-platform=wayland
--enable-features=VaapiVideoDecodeLinuxGL,AcceleratedVideoDecodeLinuxGL
--disable-features=UseChromeOSDirectVideoDecoder
--autoplay-policy=no-user-gesture-required`.
**Result: V4L2 path NOT engaged.** Chrome 147 routes the H.264 stream
through `MojoVideoDecoderService``media/filters/ffmpeg_video_decoder.cc`
(software FFmpeg). Renderer pegs at ~92 % CPU, `/dev/video0` is never
opened (`fuser` returns empty), no `V4L2VideoDecoder` /
`VaapiVideoDecoder` log lines appear at `--v=1
--vmodule="*/vaapi/*=2,*/v4l2/*=2,*video_decoder*=2,*media/gpu/*=2"`.
Compositor also fell back to software (`Switching to software
compositing.` even though panfrost render node was picked) — secondary
issue, separate from the codec wall.
**Conclusion**: 7Ji-style gn args (`use_v4l2_codec=true
use_v4lplugin=true use_linux_v4l2_only=true`) alone are insufficient
on chromium 147. The V4L2VideoDecoder factory is still gated behind
`BUILDFLAG(IS_CHROMEOS)``media/mojo/services/gpu_mojo_media_client_*.cc`
and `media/gpu/gpu_video_decode_accelerator_factory.cc` only register
the V4L2 path on ChromeOS targets.
## Validation pass 2 — 2026-04-26 22:38 UTC — V4L2VDA proven engaged
Two distinct issues were diagnosed and the codec one was fully resolved
without source surgery beyond a 2-line patch:
### Issue 1 — runtime master gate
`media::kAcceleratedVideoDecodeLinux` (user-visible feature name
"AcceleratedVideoDecoder") is hard-coded in
`media/base/media_switches.cc:750` to `FEATURE_ENABLED_BY_DEFAULT` only
when `BUILDFLAG(USE_VAAPI)` is set. On a USE_V4L2_CODEC-only build it
defaults DISABLED, the linux gpu_mojo_media_client returns
`VideoDecoderType::kUnknown`, and chrome silently falls back to
`media/filters/ffmpeg_video_decoder.cc`.
**Fix**: 2-line patch (now `patches/enable-v4l2-decoder-default.patch`):
```
-#if BUILDFLAG(USE_VAAPI)
+#if BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC)
```
The placeholder `chromeos-pipeline-bypass.patch` was deleted; PKGBUILD
now references the real patch. **Verified to apply cleanly on the CT 220
tree** (chromium 149 main).
### Issue 2 — bundled GL libs missing from tarball
The first runtime tarball shipped only `chrome` + `.pak` + locales +
`chrome_crashpad_handler`. It omitted `libEGL.so` / `libGLESv2.so`
(ANGLE) plus `libvk_swiftshader.so` and `libvulkan.so.1`. Without these,
the GPU process logs `gl::init::InitializeStaticGLBindingsOneOff failed`
and chrome falls into "Switching to software compositing." mode — which
*also* gates the V4L2 path off because the gpu_mojo_media_client never
gets a chance to dispatch.
Additionally, `--use-gl=egl` is rejected ("Requested GL implementation
gl=egl-gles2,angle=none not found in allowed implementations:
[(gl=egl-angle,angle=opengl|opengles|vulkan)]"): the build only allows
ANGLE-mediated paths. Right launcher invocation:
`--use-gl=angle --use-angle=gles`.
**Fix**: package the four libs alongside `chrome` and update the
launcher flag set. Both will be encoded in the next iteration of the
PKGBUILD's `package()` and a `chromium-fourier` launcher script.
### What we observed once both fixes were in place
With patch + bundled libs + `--enable-features=AcceleratedVideoDecoder`
+ `--use-gl=angle --use-angle=gles`, chrome on RK3566 hantro logs:
```
[gpu]: V4L2VideoDecoder()
[gpu]: Open(): No devices supporting H264 for type: 0 <- type=0 is single-planar; chrome retries multi-planar
[gpu]: InitializeBackend(): Using a stateless API for profile: h264 main and fourcc: S264
[gpu]: SetupInputFormat(): Input (OUTPUT queue) Fourcc: S264
[gpu]: AllocateInputBuffers(): Requesting: 17 OUTPUT buffers of type V4L2_MEMORY_MMAP
[gpu]: SetExtCtrlsInit(): Setting EXT_CTRLS for H264
[gpu]: SetupOutputFormat(): Output (CAPTURE queue) candidate: NV12
[gpu]: ContinueChangeResolution(): Requesting: 6 CAPTURE buffers of type V4L2_MEMORY_MMAP
[renderer]: OnDecoderSelected<video>: V4L2VideoDecoder
MediaEvent: "Selected V4L2VideoDecoder for video decoding,
config: codec: h264, profile: h264 main, [...]
coded size: [1920,1080], visible rect: [0,0,1920,1080]"
```
`fuser /dev/video1 /dev/media0` shows `chrome` (gpu pid) holding both
fds. The hantro stateless decoder is engaged. **First end-to-end
chromium-fourier V4L2 hardware decode validation: PASS** for H.264
1080p Big Buck Bunny on PineTab2.
Caveat: the render-side CPU was still ~85% during playback. Subsequent
investigation traced this to a different root cause than initially
guessed (see Pass 3 below).
## Validation pass 3 — 2026-04-26 22:50 UTC — zero-copy diagnosis
The 85 % CPU is **not** caused by software compositing or dmabuf v5
negotiation. The dmabuf-v5 warning ("Binding to zwp_linux_dmabuf_v1
version 4 but version 5 is available") is benign — chrome happily binds
to its supported max (v4). The `WaylandZwpLinuxDmabuf::OnTrancheFlags`
NOTIMPLEMENTED is also benign — KWin sends it, chrome ignores it, but
the substantive feedback (formats + modifiers) lands via
`OnTrancheFormats` / `OnTrancheTargetDevice` regardless.
Real cause: `gpu_feature_info.supports_nv12_gl_native_pixmap` ends up
**false** on this build. With it false, V4L2-decoded NV12 frames go
through the NV12-to-AR24 VPP conversion path (see
`media/mojo/services/gpu_mojo_media_client_linux.cc`
`GetPreferredRenderableFourccs` — without NV12 native pixmap support,
only `Fourcc::AR24` is added to the renderable set, forcing the VPP).
That's where the 85 % is spent.
Why is `supports_nv12_gl_native_pixmap` false?
`GLOzoneEGLWayland::CanImportNativePixmap` (in
`ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc`) requires the
chrome GL display to expose `EGL_EXT_image_dma_buf_import`. With
`--use-gl=angle --use-angle=gles`, chrome's GL display sits behind
ANGLE's EGL, and ANGLE's GLES backend on Linux does not propagate
`EGL_EXT_image_dma_buf_import` from the underlying mesa EGL up to its
clients. Verified directly: `EGL_PLATFORM=surfaceless eglinfo` on ohm
shows panfrost native EGL exposes both
`EGL_EXT_image_dma_buf_import` and `EGL_EXT_image_dma_buf_import_modifiers`
the capability is there at the panfrost layer, ANGLE just hides it.
We tried `--use-gl=egl` (direct EGL/GLES2, bypass ANGLE) but were
rejected with "Requested GL implementation (gl=egl-gles2,angle=none) not
found in allowed implementations". `WaylandSurfaceFactory::GetAllowedGLImplementations()`
in chromium 149 advertises only ANGLE-mediated impls; the
`kGLImplementationEGLGLES2` slot is missing from the list. The
`CreateViewGLSurface` dispatcher does still handle that impl — only the
*advertisement* was tightened.
### Patch 2/2 — `wayland-allow-direct-egl-gles2.patch`
3-line diff in `ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc`:
```
+ impls.emplace_back(gl::GLImplementationParts(gl::kGLImplementationEGLGLES2));
impls.emplace_back(gl::ANGLEImplementation::kOpenGL);
```
Re-allows the direct EGL/GLES2 path, ahead of the ANGLE entries so
chrome picks it by default. Verified to apply cleanly on the CT 220
tree; staged via `patch -p1` mid-rebuild (ninja's mtime-based rebuild
will pick up the change automatically).
### Outstanding for next pass (revised)
1. Rebuild lands → repackage with all four GL libs +
`chrome_crashpad_handler` + chrome → ship to ohm.
2. Validate via `chrome --use-gl=egl --ozone-platform=wayland`
`--enable-features=AcceleratedVideoDecoder` (no ANGLE shim) and
confirm `chrome://gpu` reports `Native GpuMemoryBuffers: true` and
`supports_nv12_gl_native_pixmap=true`. Target CPU during 1080p30 H.264
playback: under 30 % combined renderer + gpu.
3. If (2) passes, declare V1 of chromium-fourier shippable on ohm.
4. Add a `chromium-fourier` launcher shim under `/usr/bin/` that
defaults to `--use-gl=egl --ozone-platform=wayland`.
5. Sort the chromium 147 vs 149 confusion — the fetch went to ToT on
main rather than the 147 release branch. Either pin the branch or
accept that we're tracking ToT (probably preferable for V4L2 fixes
that are still in flight upstream).
6. Replicate end-to-end on RK3588 (ampere CoolPi or boltzmann Rock 5
ITX+) once the mainline VDPU381 driver is stable on those — those
boxes use **panthor** for Mali-G610 (Valhall), not panfrost; the
patches should be backend-agnostic but the validation is per-box.
### State of the build host (post pass 3)
- CT 220 `/build/chromium/src` patched with both
`enable-v4l2-decoder-default.patch` and
`wayland-allow-direct-egl-gles2.patch` (applied directly with
`patch -p1` mid-rebuild; ninja picks up the mtime change).
- `chromium-rebuild.service` running as a transient unit, output in
`/tmp/chromium-rebuild.log`. Most of the 93k ninja steps are cache
hits; only the patched files + their downstream objects need
recompiling.
- Tarball still on CT 220 at `/build/chromium-fourier-147.tar.gz`
(misleadingly named: it's actually 149.0.7812.0 from the main fetch,
not the 147 release tarball — separate cleanup for next pass) and on
hertz at `/tmp/chromium-fourier-147.tar.gz`. **Will be replaced by
the post-rebuild tarball once it lands.**
---
## 2026-04-28 — Patch 4 lands, KWin owns the residual stall
### Patch 4 — `nv12-external-oes-on-modifier-external-only.patch`
On panfrost / panthor, every NV12 modifier (LINEAR + AFBC ×2 + AFRC) is
flagged `external_only` in `eglQueryDmaBufModifiersEXT`. Chromium's
`OzoneImageGLTexturesHolder::GetBinding` only picked
`GL_TEXTURE_EXTERNAL_OES` when the SharedImageFormat carried
`PrefersExternalSampler` — which is set for the generic Linux multi-plane
case but **not** for V4L2 producers that arrive via the standard ozone
pixmap path. The frame then took the `GL_TEXTURE_2D` branch, ANGLE's
`validationES.cpp:4894` rejected the YUV EGLImage on a non-EXTERNAL_OES
target, the import failed, and the renderer fell back to the
NV12→AR24 software conversion (~131 % CPU baseline).
Patch closes the gap: also pick `EXTERNAL_OES` when the EGL driver
advertises the pixmap's modifier as `external_only` (cached per
`(fourcc, modifier)` tuple via a function-local
`base::flat_map`+`base::Lock`, so the EGL round-trip stays off the
per-frame hot path). Adds a single static helper
`NativePixmapEGLBinding::ModifierRequiresExternalOES`. ~+90 lines, zero
deletions, no shader changes (Skia Ganesh already handles
`GL_TEXTURE_EXTERNAL_OES` natively via `GrGLTextureInfo.fTarget`).
### Validation on ohm (RK3566 PineTab2 / hantro mainline 6.19.10)
- `bbb_1080p30_h264.mp4` plays clean, no garble, no decoder error
- Steady-state **34.7 % combined CPU** during 1080p30 H.264 (browser 12 +
GPU 9 + net 6 + render 6 + audio 1) vs v3 baseline ~131 % — **~3.8×
reduction**. Risk-1 (ANGLE+EXTERNAL_OES sampling regression on Skia
Ganesh / panfrost) **cleared**.
- `V4L2VideoDecoder()` constructor + `Using a stateless API for profile:
h264 main and fourcc: S264` confirmed in log; 6 CAPTURE buffers
V4L2_MEMORY_MMAP, NV12 output. 19 live dmabuf fds in GPU process
during steady playback — healthy V4L2 rotation + compositor depth, not
a leak.
### KWin 6.6.4 GL_ALPHA bug — separate, preexisting, blocks long playback
Across BOTH v3 (no patch 4) and v4 (with patch 4) chromium-fourier
builds, mid-playback the renderer + GPU process both park in
`futex_do_wait`, `<video>` element keeps its ⏸ icon, currentTime
advances on the audio clock, and audio outputs static (last ALSA buffer
recycled) then silence. No D-state, no v4l2/vb2/dma_fence wchan, no
error in `chrome-v[34].log`.
`journalctl` for `kwin_wayland`:
```
GL_INVALID_VALUE in glTexImage2D(internalFormat=GL_ALPHA)
GL_INVALID_OPERATION in glTexSubImage2D(invalid texture level 0) × N
```
First occurrence on this box: **2026-03-06**. KWin is requesting an
internal format that doesn't exist in modern GLES (`GL_ALPHA` is GLES1.x
legacy, not valid for `glTexImage2D` with GLES3 contexts). The
allocation fails, then every `glTexSubImage2D` to that texture errors at
level 0; KWin keeps retrying the same broken upload every frame, never
recovers. The frame-callback ack to wayland clients stalls → chrome's
renderer parks waiting for the present-feedback that never lands.
Patch 4 looks "guilty" only because of timing: with NV12 zero-copy, the
renderer is fast enough to actually post a v4l2-backed dmabuf within the
window where KWin's broken path runs; v3 was slow enough (NV12→AR24
software conversion) that the bug surfaced 510× later. **Triangulation:**
chrome v4 stalls + chrome v3 stalls + VLC `cannot convert decoder/filter
output` + mpv `could not initialize video chain` — every wayland video
client hits it; ffmpeg `-hwaccel v4l2request -f null` plays through
clean (decode path is healthy, the wall is the compositor's GL backend).
### Decoder-stack sanity post-reboot (2026-04-28 ~13:30)
After a reboot the V4L2 device numbering shuffled:
- `/dev/video0` = `rockchip,rk3568-vpu-dec` (hantro DEC, was video1)
- `/dev/video1` = `rockchip,rk3568-vepu-enc` (hantro ENC)
- `/dev/video2` = `rockchip-rga`
- `/dev/media0` = controller for DEC, `/dev/media1` = controller for ENC
Anything that hardcoded `/dev/video1` for decode now talks to the
encoder. Chrome and ffmpeg both handle this transparently (they enumerate
via media-ctl); mpv's `--hwdec=v4l2request` returns `Could not create
device` post-shuffle — separate mpv-side bug, not ours.
### Outstanding (revised, supersedes earlier list)
1. **Patch 4 lands publicly:** bump PKGBUILD `source=` and `prepare()`,
commit + tag a `chromium-fourier-149-r4` release on
`git@github.com:marfrit/chromium-fourier`.
2. **KWin pivot** — see `KWIN_PIVOT.md` (separate doc) for the plan to
identify and patch the `glTexImage2D(GL_ALPHA)` site, since ohm is the
only board on hand and every wayland video client is affected.
3. **Replication on ampere** (RK3588, panthor + rkvdec2 + hantro
multiplanar) — needs ampere woken; currently DOWN.
4. **firefox-fourier 150 build** — `firefox-fourier-150.0.1-1-aarch64.pkg.tar.zst`
is built (95 MB on workstation:/tmp/, sha256 acbf1870…), pending
fresnel power-on for V4L2 stateless validation on RK3399.