From 9994c4e3b625b04ee83ec2b82dd4f5dceb2ecef5 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Fri, 24 Apr 2026 20:03:16 +0000 Subject: [PATCH 01/88] arch/ffmpeg-v4l2-request-git: FFmpeg 8.1 + V4L2-Request hwaccel for Fourier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First carrier package for the Fourier umbrella. Tracks Kwiboo's v4l2-request-n8.1 branch (ffmpeg 8.1 base) pinned at commit b57fbbe — bump _commit to rebase onto newer tip. CI job chained after claude-his-any so it serializes on the shared aarch64 repo-db; continue-on-error: true so long ffmpeg builds don't block the debian downstream. Deliberately diverges from the AUR package: - AUR pins 6.1.1 with epoch=2 → would downgrade stock Arch ffmpeg 2:8.1-3. We track 8.1 so install is a sideways swap, not a regression. - AUR pulls X11/AMF/CUDA/FireWire/AviSynth/OpenMPT/Bluray/OpenMAX/JPEG-XL/ Theora/XVid/rsvg/soxr/ssh/vidstab/modplug/SDL2/Vulkan/JACK/GSM/Speex — dropped here; none are needed on a Wayland ARM video-decode fleet. - AUR uses #branch=…, sha256sums=(SKIP) → every build is tip-of-branch, not reproducible. We pin via #commit=. Kept: encoders (libx264/libx265/libvpx/libdav1d), VAAPI, libdrm, libv4l2, neon, OpenGL, PulseAudio, subtitle/font stack, gnutls TLS. SDL2 dropped means no ffplay binary (mpv covers interactive playback). provides=(ffmpeg) conflicts=(ffmpeg) so it replaces stock ffmpeg on the target host deliberately. Primary consumers: ohm (Fourier step 5), fresnel, ampere. --- .gitea/workflows/build.yml | 111 ++++++++++++++++++++ arch/ffmpeg-v4l2-request-git/PKGBUILD | 139 ++++++++++++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 arch/ffmpeg-v4l2-request-git/PKGBUILD diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 77076a3510..80e1e0a013 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -371,6 +371,117 @@ jobs: if: always() run: rm -f /root/repo_pass /root/.ssh/id_ed25519 + # ------------------------------------------------------------------------- + # ffmpeg-v4l2-request-git (aarch64 only) — FFmpeg with V4L2 Request API + # hwaccel for Rockchip / Allwinner stateless decoders. Used by the Fourier + # umbrella (ohm, fresnel, ampere). Long-running build, so failures don't + # block downstream debian jobs. + # ------------------------------------------------------------------------- + ffmpeg-v4l2-request-aarch64: + needs: claude-his-any + runs-on: arch-aarch64 + continue-on-error: true + steps: + - uses: actions/checkout@v4 + + - name: bootstrap runner (idempotent) + run: | + set -e + retry() { for i in 1 2 3; do "$@" && return 0; rc=$?; echo "retry $i (exit=$rc)" >&2; sleep $((i*5)); done; return 1; } + retry pacman -Syu --noconfirm --needed base-devel git rsync gnupg openssh sudo nasm + + - name: import signing key + env: + PRIV: ${{ secrets.MARFRIT_REPO_PRIVATE_KEY }} + PASS: ${{ secrets.MARFRIT_REPO_PASSPHRASE }} + run: | + set -e + rm -rf /root/.gnupg /root/repo_pass + mkdir -m700 -p /root/.gnupg + printf '%s' "$PASS" > /root/repo_pass + chmod 600 /root/repo_pass + printf '%s\n' "$PRIV" | gpg --batch --import + echo "92D5E96D8F63C75E4116AA1FF5C8C4603D0D250C:6:" | gpg --import-ownertrust + + - name: install deploy ssh key + env: + KEY: ${{ secrets.MARFRIT_REPO_DEPLOY_KEY }} + run: | + mkdir -m700 -p /root/.ssh + printf '%s\n' "$KEY" > /root/.ssh/id_ed25519 + chmod 600 /root/.ssh/id_ed25519 + ssh-keyscan -t ed25519 nc.reauktion.de > /root/.ssh/known_hosts 2>/dev/null + + - name: makepkg ffmpeg-v4l2-request-git + run: | + set -e + rm -rf /tmp/build-ffmpeg-v4l2 + cp -r arch/ffmpeg-v4l2-request-git /tmp/build-ffmpeg-v4l2 + chown -R builder:builder /tmp/build-ffmpeg-v4l2 + cd /tmp/build-ffmpeg-v4l2 + # Parallelise: fermi has 4 A55 cores; -j4 fits RAM during link. + sudo -u builder -H env MAKEFLAGS="-j$(nproc)" \ + makepkg --nocheck --noconfirm --syncdeps --cleanbuild + ls -la *.pkg.tar.* | grep -v "\.sig$" + + - name: sign ffmpeg-v4l2-request-git + run: | + set -e + cd /tmp/build-ffmpeg-v4l2 + for f in *.pkg.tar.xz *.pkg.tar.zst *.pkg.tar.gz; do + [ -f "$f" ] || continue + gpg --batch --pinentry-mode loopback --passphrase-file /root/repo_pass \ + --detach-sign --yes -u 92D5E96D8F63C75E4116AA1FF5C8C4603D0D250C "$f" + done + + - name: update aarch64 repo db + run: | + set -e + mkdir -p /tmp/arch-stage-ffmpeg + cd /tmp/arch-stage-ffmpeg + rm -f * + for f in marfrit.db.tar.gz marfrit.db.tar.gz.sig marfrit.files.tar.gz marfrit.files.tar.gz.sig; do + curl -sSLf "https://packages.reauktion.de/arch/aarch64/$f" -o "$f" || rm -f "$f" + done + for ext in xz zst gz; do + ls /tmp/build-ffmpeg-v4l2/*.pkg.tar.$ext 2>/dev/null && \ + mv /tmp/build-ffmpeg-v4l2/*.pkg.tar.$ext /tmp/build-ffmpeg-v4l2/*.pkg.tar.$ext.sig . + done || true + export GNUPGHOME=/root/.gnupg + printf 'pinentry-mode loopback\npassphrase-file /root/repo_pass\n' > /root/.gnupg/gpg.conf + printf 'allow-loopback-pinentry\n' > /root/.gnupg/gpg-agent.conf + gpg-connect-agent reloadagent /bye + pkgs=() + for ext in xz zst gz; do + for f in *.pkg.tar.$ext; do [ -f "$f" ] && pkgs+=("$f"); done + done + if [ -f marfrit.db.tar.gz ]; then + for f in "${pkgs[@]}"; do + name=$(echo "$f" | sed -E 's/-[0-9].*//') + repo-remove --sign --key 92D5E96D8F63C75E4116AA1FF5C8C4603D0D250C \ + marfrit.db.tar.gz "$name" 2>/dev/null || true + done + fi + repo-add --new --sign --key 92D5E96D8F63C75E4116AA1FF5C8C4603D0D250C \ + --verify marfrit.db.tar.gz "${pkgs[@]}" + ln -sf marfrit.db.tar.gz marfrit.db + ln -sf marfrit.files.tar.gz marfrit.files + ln -sf marfrit.db.tar.gz.sig marfrit.db.sig + rm -f marfrit.files.sig + + - name: publish to aarch64 + run: | + set -e + retry() { for i in 1 2 3; do "$@" && return 0; rc=$?; echo "retry $i (exit=$rc)" >&2; sleep $((i*5)); done; return 1; } + cd /tmp/arch-stage-ffmpeg + retry rsync -avL --copy-unsafe-links \ + -e 'ssh -i /root/.ssh/id_ed25519' \ + ./ mfritsche@nc.reauktion.de:arch/aarch64/ + + - name: wipe secrets + if: always() + run: rm -f /root/repo_pass /root/.ssh/id_ed25519 + # ------------------------------------------------------------------------- # claude-his-agent Debian (Architecture: all) — same dpkg-deb pattern as # lmcp-debian; publishes to bookworm + trixie via hertz reprepro. diff --git a/arch/ffmpeg-v4l2-request-git/PKGBUILD b/arch/ffmpeg-v4l2-request-git/PKGBUILD new file mode 100644 index 0000000000..8be9ee24fc --- /dev/null +++ b/arch/ffmpeg-v4l2-request-git/PKGBUILD @@ -0,0 +1,139 @@ +# Maintainer: Markus Fritsche +# +# FFmpeg + V4L2-Request hwaccel (stateless video decode on Rockchip, +# Allwinner, etc) for the Fourier umbrella. Tracks Kwiboo's long-running +# rebase of Jernej Škrabec's v4l2-request patchset onto ffmpeg release +# tags. Pins the branch tip to a known commit for reproducible CI builds; +# bump _commit when upstream picks up a fix we want. +# +# Why this fork instead of AUR ffmpeg-v4l2-request-git: +# - AUR is pinned to 6.1.1 with epoch=2, which is OLDER than Arch's +# stock 2:8.1-3 → installing it downgrades system ffmpeg. +# - AUR pulls X11/AMF/CUDA/FireWire/AviSynth/OpenMPT/Bluray — irrelevant +# for a Wayland + ARM + video-decode fleet. +# - AUR uses #branch=..., no commit pin. CI artifacts are non-reproducible. +# +# Encoders (libx264/libx265/libvpx/libdav1d) kept per Fourier fleet policy. +# X11, AMF, CUDA, FireWire, AviSynth, OpenMPT, Bluray, OpenMAX, JPEG-XL, +# Theora, XVid, rsvg, soxr, ssh, vidstab, modplug, SDL2, Vulkan, JACK, GSM, +# Speex dropped — not needed on the Fourier fleet. (No SDL2 means no +# `ffplay` binary; mpv covers interactive playback.) + +pkgname=ffmpeg-v4l2-request-git +_srcname=FFmpeg +_version='8.1' +_commit='b57fbbe50c9b2656fad86a1a7eeabfd2b2a50935' # v4l2-request-n8.1 tip 2026-04-24 +pkgver=8.1.r0.b57fbbe +pkgrel=1 +epoch=2 +pkgdesc='FFmpeg with V4L2 Request API hwaccel (Rockchip / Allwinner stateless decode)' +arch=('aarch64') +url='https://github.com/Kwiboo/FFmpeg' +license=('GPL-3.0-or-later') +depends=( + alsa-lib + bzip2 + fontconfig + fribidi + gmp + gnutls + lame + libass.so + libdav1d.so + libdrm + libfreetype.so + libgl + libpulse + libva.so + libva-drm.so + libvorbis.so + libvorbisenc.so + libvpx.so + libwebp + libx264.so + libx265.so + libxml2 + opus + v4l-utils + xz + zlib +) +makedepends=( + git + linux-api-headers + mesa + nasm +) +provides=( + libavcodec.so + libavdevice.so + libavfilter.so + libavformat.so + libavutil.so + libpostproc.so + libswresample.so + libswscale.so + ffmpeg +) +conflicts=(ffmpeg) +source=("git+https://github.com/Kwiboo/FFmpeg.git#commit=${_commit}") +sha256sums=('SKIP') + +pkgver() { + cd "${_srcname}" + printf '%s.r%s.%s' "${_version}" \ + "$(git rev-list --count HEAD)" \ + "$(git rev-parse --short=7 HEAD)" +} + +build() { + cd "${_srcname}" + + ./configure \ + --prefix=/usr \ + --disable-debug \ + --disable-static \ + --disable-doc \ + --disable-stripping \ + --enable-shared \ + --enable-gpl \ + --enable-version3 \ + --enable-pic \ + --enable-neon \ + --arch=aarch64 \ + \ + --enable-libdrm \ + --enable-libv4l2 \ + --enable-libudev \ + --enable-v4l2-request \ + --enable-v4l2_m2m \ + --enable-vaapi \ + --enable-opengl \ + \ + --enable-gnutls \ + --enable-fontconfig \ + --enable-libass \ + --enable-libfreetype \ + --enable-libfribidi \ + --enable-libxml2 \ + --enable-libpulse \ + --enable-libdav1d \ + --enable-libopus \ + --enable-libvorbis \ + --enable-libmp3lame \ + --enable-libvpx \ + --enable-libx264 \ + --enable-libx265 \ + --enable-libwebp \ + \ + --host-cflags='-fPIC' + + make + make tools/qt-faststart +} + +package() { + cd "${_srcname}" + make DESTDIR="${pkgdir}" install + install -Dm755 tools/qt-faststart "${pkgdir}/usr/bin/qt-faststart" +} From f4bdd54543d47bb436e4fada5c04b540a96b387e Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Sat, 25 Apr 2026 22:51:01 +0000 Subject: [PATCH 02/88] arch/chromium-fourier: workspace + STUDY.md (no PKGBUILD yet) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stage the next side-project: patch upstream Chromium to do HW video decode through VaapiVideoDecoder + our marfrit/libva-v4l2-request-fourier backend on a mainline Linux Wayland system, instead of going through the chromeos pipeline that fails on Brave today. STUDY.md captures: - The exact failure stack we're fixing (PickDecoderOutputFormat -> ImageProcessor init failure in media/gpu/chromeos/video_decoder_pipeline.cc) - Three candidate patches (chromeos pipeline bypass, V4L2VideoDecoder factory un-gate, libva backend default) - Reference forks (JeffyCN, igel-oss, 7Ji-PKGBUILDs/chromium-mpp, amazingfate/chromium-debian-build) — all use the older V4L2VDA path with vendor MPP, not VAAPI; useful for PKGBUILD shape and factory-un-gating patterns but not directly applicable - Build plan on fermi (depot_tools, ~30 GB fetch, 6-10 h initial build, distcc-avahi acceleration through CT108 + tesla) - Phase order — workspace done now, build env next session, patches after that, package after that, brave-fourier rebase last No PKGBUILD added yet; one will land when there's something to actually package. Build artifacts intentionally not in repo (chromium tree is ~100 GB). Co-Authored-By: Claude Opus 4.7 (1M context) --- arch/chromium-fourier/STUDY.md | 165 +++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 arch/chromium-fourier/STUDY.md diff --git a/arch/chromium-fourier/STUDY.md b/arch/chromium-fourier/STUDY.md new file mode 100644 index 0000000000..bc6dba1199 --- /dev/null +++ b/arch/chromium-fourier/STUDY.md @@ -0,0 +1,165 @@ +# chromium-fourier — Chromium with V4L2-stateless / VAAPI HW decode on mainline-kernel ARM Linux + +## Goal + +Patch upstream Chromium so it can do hardware video decode through +`VaapiVideoDecoder` on a plain Linux Wayland system on Rockchip +(RK3566 / RK3588), via our `marfrit/libva-v4l2-request-fourier` libva +backend. Target consumers: Brave (rebuilt from the same patched +Chromium), upstream Chromium itself for Arch Linux ARM, and any other +Blink-based browser that picks up the patches. + +This fills a niche **no shipping fork** currently fills — every existing +ARM-Linux Chromium with HW decode (7Ji's `chromium-mpp`, Ubuntu's +`liujianfeng1994/rockchip-multimedia` PPA, JeffyCN's recipe, etc.) goes +through the **vendor MPP path** on **5.10 BSP kernel + X11 + Mali blob ++ panfork**. We're going for **mainline kernel + V4L2 stateless + +Wayland + panfrost / panthor**, the direction Fourier is heading anyway. + +## What's actually broken in stock Chromium 147+ + +1. **`media/gpu/chromeos/video_decoder_pipeline.cc`** is selected on Linux + when it shouldn't be. It then tries to initialize a V4L2 + `ImageProcessor` (a ChromeOS-specific m2m chip block for color + conversion / scaling) that doesn't exist on a plain Linux Wayland + system. Failure surface: + + ``` + PickDecoderOutputFormat(): Initializing ImageProcessor; max buffers: 16 + ERROR: failed Initialize()ing the frame pool + ``` + +2. **`VaapiVideoDecoder`** *is* compiled into stock Chromium / brave-bin + (verified: `strings /opt/brave-bin/brave | grep VaapiVideoDecoder`), + but the chromeos pipeline preempts it. Once the pipeline fails, the + decoder selection bails — it never falls back to direct + `VaapiVideoDecoder`. + +3. **`V4L2VideoDecoder`** factory is `BUILDFLAG(IS_CHROMEOS)`-gated. + Feature flags `UseChromeOSDirectVideoDecoder` / + `V4L2FlatStatelessVideoDecoder` are no-ops on a non-ChromeOS build — + their flag strings don't even appear in the binary. + +## The patch shape (sketch) + +### Patch 1 — bypass the chromeos pipeline on Linux + +`media/gpu/chromeos/video_decoder_pipeline.cc` is reached from +`media/gpu/vaapi/vaapi_video_decoder.cc` somewhere in +`ApplyResolutionChangeWithScreenSizes`. Either: + +- Skip the chromeos pipeline entirely on Linux non-ChromeOS, going + straight to `VaapiVideoDecoder::CreateContextAndScopedVASurfaces` for + frame allocation, OR +- Replace `PickDecoderOutputFormat`'s ImageProcessor probing with a + no-op that just trusts the VAAPI surface format (since on real + ChromeOS the IP does color conversion that we don't need in our + dmabuf-passthrough path). + +Reference: [crbug 40192819] "VaapiVideoDecoder on linux" +(). + +### Patch 2 — un-gate `V4L2VideoDecoder` factory for non-ChromeOS Linux + +Optional but useful as a fallback. The class is compiled in; only the +factory registration is `IS_CHROMEOS`-gated. The `igel-oss/meta-browser-hwdecode` +patch +shows the historical pattern for the V4L2VDA factory; the modern factory +is in `media/gpu/v4l2/v4l2_video_decoder.{h,cc}`. + +This is **plan B** if Patch 1 turns out harder than expected — V4L2VDA +talks to `/dev/videoN` directly without going through libva, which means +we don't depend on the `libva-v4l2-request-fourier` decode-submission +path either. Shorter end-to-end critical path, but a more invasive +factory change. + +### Patch 3 — point libva at our backend (build-time) + +Already configurable at runtime via `LIBVA_DRIVER_NAME=v4l2_request`, +but a Brave/Chromium binary that defaults to it would be cleaner. Could +patch the GPU process startup to set the env var if not already set. +Cosmetic, optional. + +## Reference forks (read these side-by-side with our patch) + +- **JeffyCN/meta-rockchip chromium recipe** — the upstream of the V4L2VDA + factory un-gating and `libv4l-rkmpp` shim that every shipping fork + uses. + Caveat: targets the V4L2VDA path (Plan B), not the modern + `VaapiVideoDecoder` (Plan A) we want. +- **igel-oss/meta-browser-hwdecode** — Yocto layer with the original + `0001-Add-support-for-V4L2VDA-on-Linux.patch`. 2017-vintage but the + pattern still applies. +- **7Ji-PKGBUILDs/chromium-mpp** — the most recent ALARM-shipping + variant, Chromium 132 with MPP. Useful for the **PKGBUILD shape** and + the patch-set list, even though we're not using MPP. + +- **amazingfate/chromium-debian-build** — Debian flavour of the same + approach, for reference. + +## Build environment + +- **Source tree**: `gn` / `depot_tools`, hosted on **fermi** (Arch ARM + aarch64 LXC on hertz). Fetch is ~30 GB; full chromium tree is ~100 GB + with build artifacts. Fermi's storage budget needs checking — may + need to bind-mount a hertz path with more headroom. +- **Build acceleration**: `distcc-avahi` is already deployed. Wire the + chromium build to use distcc through CT108 + tesla as compile workers. + Pump mode can shave further; chromium's ninja will accept distcc'd + cc/c++ via wrappers. +- **First build wall time estimate**: 6–10 hours initial on fermi alone; + 3–5 hours with distcc-avahi if the network throughput holds. After + the first build, incrementals on small patches are ~10–15 min. +- **Configure flags**: `gn args` — start from Arch's `chromium` + PKGBUILD, add `use_vaapi=true use_v4l2_codec=true is_official_build=false`. +- **Output**: `chromium-fourier--1-aarch64.pkg.tar.zst` + shipping `/usr/bin/chromium`. `provides=(chromium) conflicts=(chromium)` + shape, same as `ffmpeg-v4l2-request-git`'s replacement of stock ffmpeg. + +## Validation path + +Once a patched binary builds: + +1. Launch with the same env we use for mpv-vaapi: + `LIBVA_DRIVER_NAME=v4l2_request LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video0` +2. Open `chrome://gpu` — should show "Video Decode: Hardware accelerated" + (not "Disabled" or "Software only"). +3. Open a 1080p H.264 file:// URL, watch CPU on `/dev/video0` openers, + measure Brave's GPU process CPU% during playback. +4. Cross-reference with `mpv --hwdec=vaapi` numbers once the + libva-v4l2-request-fourier decode-submission path also lands. + +## Order of operations + +Phase A (this session): workspace + STUDY.md (this file). **Done.** + +Phase B: build environment — install `depot_tools` on fermi, run +`fetch chromium`, get a baseline (unpatched) build to confirm fermi can +build chromium at all. ~one full day. + +Phase C: identify the exact line to flip for Patch 1 by reading +`media/gpu/chromeos/video_decoder_pipeline.cc` + +`media/gpu/vaapi/vaapi_video_decoder.cc` against current Chromium +master. Iterate the patch on a real build. + +Phase D: package as `chromium-fourier` PKGBUILD, hook into +marfrit-packages CI on fermi (already has the pattern from +ffmpeg-v4l2-request-git). + +Phase E: rebase Patch 1 (and Patch 2 if needed) onto Brave's source +tree, ship as `brave-fourier` next to `chromium-fourier`. Brave's tree +adds ~50 patches on top of upstream Chromium; the chromeos-pipeline +seam should be unchanged across that delta, so this should be a +mechanical rebase. + +## Out of scope + +- HW video **encode** (cameras, webcam streams). RK3566 has a separate + encoder block on `/dev/video2`; not a Fourier priority. +- HEVC / VP9 / AV1 — RK3566 has no HW for these. RK3588 has VDPU381 + but our libva backend doesn't speak HEVC yet. +- WebGL / WebGPU performance — separate concern, not part of video + decode. +- Brave-specific features (Shields, Wallet, etc.) — they all live in + Brave's source tree on top of Chromium and are unaffected by our + decode patches. From 055fc0ba06b9abe2f9f2a82b11eb481319d68969 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Sat, 25 Apr 2026 23:28:12 +0000 Subject: [PATCH 03/88] arch/chromium-fourier: scaffold PKGBUILD + placeholder patch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initial chromium-fourier shape on the chromium-builder@boltzmann LXD container we provisioned today. Approach is the 7Ji-style "tarball + system tools" pattern (no gclient/CIPD, the linux-arm64 dependency binaries don't exist anyway) but stripped of the MPP/X11/panfork specifics — chromium-fourier targets mainline kernel + Wayland + panfrost/panthor + V4L2 stateless on /dev/video0, not the vendor stack 7Ji's chromium-mpp targets. PKGBUILD highlights: - pkgver=147.0.7727.116 (current Chrome stable as of 2026-04-25) - gn args: use_v4l2_codec=true, use_v4lplugin=true, use_linux_v4l2_only=true, use_vaapi=true. The first three are the magic that unlocks V4L2VDA on Linux non-ChromeOS without source patches; if they're sufficient on their own, the chromeos-pipeline-bypass patch stays a no-op. - ffmpeg_branding="Chrome" + proprietary_codecs=true for H.264. - enable_widevine=false, enable_nacl=false to keep the tree small. - Currently development-shaped: prepare()/build() operate on a pre-extracted /build/chromium/src rather than makepkg-fetched source. Will switch to canonical source=(...tarball.xz) shape once the patches stabilise. patches/chromeos-pipeline-bypass.patch is a placeholder; the actual patch (if any) gets developed once we see what 7Ji's gn args do or don't unlock for us. Co-Authored-By: Claude Opus 4.7 (1M context) --- arch/chromium-fourier/PKGBUILD | 166 ++++++++++++++++++ .../patches/chromeos-pipeline-bypass.patch | 22 +++ 2 files changed, 188 insertions(+) create mode 100644 arch/chromium-fourier/PKGBUILD create mode 100644 arch/chromium-fourier/patches/chromeos-pipeline-bypass.patch diff --git a/arch/chromium-fourier/PKGBUILD b/arch/chromium-fourier/PKGBUILD new file mode 100644 index 0000000000..3bc9aaa03f --- /dev/null +++ b/arch/chromium-fourier/PKGBUILD @@ -0,0 +1,166 @@ +# Maintainer: Markus Fritsche +# +# Chromium with V4L2 HW video decode unlocked on Linux for Rockchip +# (RK3566 hantro / RK3588 VDPU381) on **mainline** kernel + Wayland + +# panfrost / panthor — no vendor MPP, no Mali blob, no panfork, no +# 5.10 BSP kernel. Fills the niche that 7Ji's chromium-mpp explicitly +# does not (it forces BSP + X11 + vendor stack); see +# /home/mfritsche/src/marfrit-packages/arch/chromium-fourier/STUDY.md +# for the full rationale. +# +# Build host: chromium-builder LXD container on boltzmann (8 cores, +# 28 GB RAM cap, 824 GB NVMe). 6-10 h initial build. + +pkgname=chromium-fourier +pkgver=147.0.7727.116 +pkgrel=1 +epoch=1 +pkgdesc='Chromium with V4L2VDA HW video decode unlocked for mainline Linux Wayland on Rockchip' +arch=('aarch64') +url='https://www.chromium.org/Home' +license=('BSD-3-Clause') +depends=( + alsa-lib + at-spi2-core + cairo + cups + dbus + fontconfig + freetype2 + gtk3 + hicolor-icon-theme + libdrm + libpulse + libva + libxcb + libxkbcommon + mesa + nspr + nss + pango + pciutils + pipewire + ttf-liberation + v4l-utils + wayland +) +makedepends=( + clang + elfutils + gn + gperf + java-runtime-headless + libxslt + lld + ninja + nodejs + npm + python + qt5-base + qt6-base + re2 + rust + rust-bindgen +) +optdepends=( + 'qt6-base: for Qt6 toolkit integration' +) +provides=(chromium) +conflicts=(chromium) +options=('!lto' '!strip') +# NB: the chromium tarball is fetched lazily by build.sh on the build +# host (5.7 GB compressed, doesn't belong in this git repo). The +# PKGBUILD assumes /build/chromium/src is already populated. +source=( + 'patches/chromeos-pipeline-bypass.patch' +) +sha256sums=( + 'SKIP' +) + +# NB: this PKGBUILD is currently development-shaped — it operates on a +# pre-extracted chromium tree at /build/chromium/src rather than letting +# makepkg fetch + unpack the 5.7 GB tarball into ${srcdir}. Once the +# patches stabilise we'll switch to the canonical pattern with +# source=(${url}/chromium-${pkgver}.tar.xz ...) so the standard makepkg +# flow + CI fermi-style pipeline work end-to-end. + +prepare() { + cd /build/chromium/src + + # Fourier-local: bypass the chromeos pipeline so VaapiVideoDecoder / + # V4L2VDA is reachable on Linux non-ChromeOS. The 7Ji-style gn args + # (use_v4l2_codec / use_v4lplugin / use_linux_v4l2_only) may be + # sufficient on their own; this patch is the fallback if they aren't. + patch -Np1 -i "${srcdir}/patches/chromeos-pipeline-bypass.patch" || true + + # Use system node, system java + rm -f third_party/node/linux/node-linux-arm64/bin/node + mkdir -p third_party/node/linux/node-linux-arm64/bin + ln -sf /usr/bin/node third_party/node/linux/node-linux-arm64/bin/ + ln -sf /usr/bin/java third_party/jdk/current/bin/ 2>/dev/null || true +} + +build() { + cd /build/chromium/src + + local _flags=( + 'is_official_build=true' + 'is_debug=false' + 'symbol_level=0' + 'is_cfi=false' + 'treat_warnings_as_errors=false' + 'enable_nacl=false' + 'enable_widevine=false' + + # System toolchain (clang/lld from pacman) + 'custom_toolchain="//build/toolchain/linux/unbundle:default"' + 'host_toolchain="//build/toolchain/linux/unbundle:default"' + 'use_sysroot=false' + 'use_custom_libcxx=true' + + # The whole point of chromium-fourier — V4L2 HW decode on Linux + 'use_v4l2_codec=true' + 'use_v4lplugin=true' + 'use_linux_v4l2_only=true' + 'use_vaapi=true' + + # Codec branding for proprietary codec support (H.264 etc.) + 'ffmpeg_branding="Chrome"' + 'proprietary_codecs=true' + + 'rtc_use_pipewire=true' + 'link_pulseaudio=true' + 'use_qt6=true' + 'moc_qt6_path="/usr/lib/qt6"' + ) + + gn gen out/Default --args="${_flags[*]}" + autoninja -C out/Default chrome chromedriver chrome_sandbox +} + +package() { + cd /build/chromium/src + + install -Dm755 out/Default/chrome "${pkgdir}/usr/lib/chromium/chromium" + install -Dm4755 out/Default/chrome_sandbox "${pkgdir}/usr/lib/chromium/chrome-sandbox" + install -Dm755 out/Default/chromedriver "${pkgdir}/usr/bin/chromedriver" + + # Resources / locales / pak files + for f in chrome_100_percent.pak chrome_200_percent.pak resources.pak \ + v8_context_snapshot.bin icudtl.dat headless_lib_data.pak \ + headless_lib_strings.pak; do + [ -f "out/Default/$f" ] && install -Dm644 "out/Default/$f" \ + "${pkgdir}/usr/lib/chromium/$f" + done + + # Locales + if [ -d out/Default/locales ]; then + install -dm755 "${pkgdir}/usr/lib/chromium/locales" + cp -r out/Default/locales/* "${pkgdir}/usr/lib/chromium/locales/" + fi + + # Top-level launcher + install -dm755 "${pkgdir}/usr/bin" + ln -s /usr/lib/chromium/chromium "${pkgdir}/usr/bin/chromium" +} diff --git a/arch/chromium-fourier/patches/chromeos-pipeline-bypass.patch b/arch/chromium-fourier/patches/chromeos-pipeline-bypass.patch new file mode 100644 index 0000000000..d2d5d458ab --- /dev/null +++ b/arch/chromium-fourier/patches/chromeos-pipeline-bypass.patch @@ -0,0 +1,22 @@ +From: Markus Fritsche +Subject: media/gpu: skip chromeos VideoDecoderPipeline on non-ChromeOS Linux + +Placeholder. The patch will be developed against the actual chromium +147.0.7727.116 source tree on chromium-builder@boltzmann once the +tarball is extracted and we can read the exact code paths in: + + - media/gpu/chromeos/video_decoder_pipeline.cc (PickDecoderOutputFormat, + Initialize, ImageProcessor setup) + - media/gpu/vaapi/vaapi_video_decoder.cc (ApplyResolutionChangeWithScreenSizes, + line ~1219 where "failed Initialize()ing the frame pool" fires) + +The 7Ji-style gn args (`use_v4l2_codec=true use_v4lplugin=true +use_linux_v4l2_only=true`) MAY be sufficient by themselves to route +decode through the legacy V4L2VDA path entirely, bypassing the +chromeos pipeline without source patches. First build will tell us; +this file is the placeholder for the patch we'll need if it isn't. + +# Empty no-op patch. patch -p1 < this won't change anything. +diff --git a/PLACEHOLDER b/PLACEHOLDER +new file mode 100644 +index 0000000..e69de29 From f3a7b8f1c1ad751de30ee90c0e4c7fb31366ed69 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Sun, 26 Apr 2026 01:02:24 +0000 Subject: [PATCH 04/88] chromium-fourier NEXT.md: hit the clang 22 vs 147 wall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First-build summary on chromium-builder@boltzmann. gn gen succeeds with our V4L2VDA-unlock args. ninja fails immediately on: 1. chromium 147 emits clang flags (-fno-lifetime-dse, -fsanitize-ignore-for-ubsan-feature=array-bounds) that clang 22 doesn't know. Arch Linux ARM is on clang 22; clang 23 hasn't landed in extra yet. 2. Bundled x86_64 esbuild is invoked via qemu-x86_64-static but /lib64/ld-linux-x86-64.so.2 isn't installed — same shape as the bundled node-linux-x64 issue we already fixed by symlinking to system node. Smaller wall. Documents 5 paths forward (grind patches / pin chromium 132 (7Ji's known-good) / pin 138-141 middle ground / use chromium's bundled clang / wait for Arch ARM clang 23) with estimated effort and trade- offs. Recommends pinning to a chromium version that compiles clean against clang 22 as the fastest path to a working browser, then bumping as Arch ARM bumps clang. Build host state preserved — container running, source extracted, gn-gen'd, no compile artifacts. Easy to resume from any of the five paths. Co-Authored-By: Claude Opus 4.7 (1M context) --- arch/chromium-fourier/NEXT.md | 110 ++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 arch/chromium-fourier/NEXT.md diff --git a/arch/chromium-fourier/NEXT.md b/arch/chromium-fourier/NEXT.md new file mode 100644 index 0000000000..9928e4ada8 --- /dev/null +++ b/arch/chromium-fourier/NEXT.md @@ -0,0 +1,110 @@ +# chromium-fourier — first-build status (2026-04-26 00:42 UTC) + +## Where we are + +Build environment: **chromium-builder LXD container on boltzmann** +(8 cores, 28 GB RAM cap, 824 GB NVMe, Beryllium OS rkr3 host kernel). +Source: chromium-147.0.7727.116 release tarball extracted at +`/build/chromium/src` (25 GB extracted). + +`gn gen out/Default` **succeeds** with our 7Ji-style args +(`use_v4l2_codec=true use_v4lplugin=true use_linux_v4l2_only=true +use_vaapi=false`, system toolchain via `unbundle:default`, system clang +at `/usr/bin/clang`, version-symlink `/usr/lib/clang/23` → +`/usr/lib/clang/22`, compiler-rt-adjust-paths style suffix patch +manually applied). 28057 targets generated. + +`ninja -C out/Default chrome` **fails immediately** with two distinct walls: + +### Wall 1 — clang version mismatch (chromium 147 ↔ Arch clang 22) + +Chromium 147's compile flags include +`-fno-lifetime-dse` and +`-fsanitize-ignore-for-ubsan-feature=array-bounds`. Both are clang 23+ +features. Arch Linux ARM ships **clang 22.1.3** (extra repo). Every +single C++ compile fails with `clang++: error: unknown argument`. + +Resolutions, in order of effort: + +- **(a)** Wait for Arch ARM to bump clang to 23. Tracking package + upstream — happens whenever LLVM 23 lands in extra. Days to weeks. +- **(b)** Use chromium's bundled clang via + `tools/clang/scripts/update.py`. That hits the same CIPD/gs:// + "linux-arm64 isn't a first-class target" issue we saw with + `gclient sync` earlier — chromium's clang prebuilts are x86_64-only + for many platforms. +- **(c)** Fork an older chromium (e.g., 132 or 138) that compiles + cleanly with clang 22. 7Ji's chromium-mpp PKGBUILD targets 132 and + builds clean on Arch ARM today. Loses 15 versions of upstream + chromium evolution but ships fast. +- **(d)** Patch chromium 147 to drop the offending flags + (`build/config/compiler/BUILD.gn` has the cflags lists). 50–200 line + patch, brittle across version bumps but tractable. Fights every + rebase. + +### Wall 2 — bundled x86_64 esbuild under qemu + +After Wall 1 (or independently for Action targets): +`qemu-x86_64-static: Could not open '/lib64/ld-linux-x86-64.so.2'` +when chromium runs the bundled x86_64 `esbuild` from +`third_party/devtools-frontend/.../scripts/build/typescript/ts_library.py`. +Same shape as the bundled `node-linux-x64` issue we already fixed (we +symlinked system node into that path). `esbuild` needs the same +treatment — install system esbuild via `npm install -g esbuild` and +symlink it into the path chromium expects. Or install `qemu-user-static` ++ `glibc-x86_64` to make the bundled binary actually run. + +**Wall 2 is much smaller than Wall 1** — a handful of bundled-x86_64 +binaries to identify and replace, vs. fundamental clang version mismatch. + +## What worked + +- LXD container provisioning on boltzmann via his recommendation — + the host environment is right. +- Tarball-instead-of-gclient approach — sidesteps CIPD-doesn't-have- + linux-arm64 problem for source acquisition, leaves only a few + bundled binary issues at build time. +- Wall 1 / Wall 2 are both **identifiable and bounded**. We're past + the "is this even doable" phase; this is now down to grinding the + patches. + +## Options — needs your call + +1. **Grind through Wall 1 with patches** — patch + `build/config/compiler/BUILD.gn` to drop flags clang 22 doesn't + know. Iterate per build error. Estimated 5–15 patch-and-retry + cycles to compile clean. Then 6–10 h actual build. +2. **Pin to chromium 132** — match 7Ji's known-working version on + Arch ARM. Drop our STUDY focus on "current upstream Chromium" and + ship a 1-year-old binary. Build should work much sooner. +3. **Pin to chromium 138 or 140** — middle ground. Likely uses clang + 22 features and not 23. Some research needed to find the cutover. +4. **Use chromium's bundled clang** — not viable on linux-arm64 + without extensive sysroot setup; same CIPD issue as gclient sync. +5. **Wait for Arch ARM clang 23** — passive, days-to-weeks horizon. + +Recommended (FWIW): **start with (3)** — find the latest chromium +version that builds clean against clang 22 (probably 138-141 range), +ship that as `chromium-fourier`, then bump as Arch ARM bumps clang. +That gives us a working browser in a few hours rather than days, on +mainline Linux + Wayland + V4L2 unlock — which is the actual goal. +The "current upstream Chromium" requirement was nice-to-have, not +essential. + +## State of the build host (preserved) + +- Container: `chromium-builder` on boltzmann (running, idle) +- Source: `/build/chromium/src` (extracted tarball, 25 GB) +- Build dir: `/build/chromium/src/out/Default` (gn-gen'd, no artifacts) +- Tools installed: gn, ninja, clang 22, lld, gperf, nodejs (system), + rust, qt5/6, all the gtk/wayland/va/v4l deps from the long pacman + shopping list +- Patches applied to source: `compiler-rt-adjust-paths` style (manual) +- Symlinks: `/usr/lib/clang/23` → `/usr/lib/clang/22`, + `third_party/node/linux/node-linux-x64/bin/node` → `/usr/bin/node` +- Service unit history: `chromium-fetch.service` (one-shot, succeeded + on tarball + extract); `chromium-build.service` (one-shot, three + failed attempts above). + +Discard the container and start over with option 2 if you pick that +direction; otherwise iterate from current state. From 7bb2fbeca9f553fe70f1bacb5764772e97e65689 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Sun, 26 Apr 2026 05:57:14 +0000 Subject: [PATCH 05/88] chromium-fourier: pivot to x86_64 cross-compile from CT220 on data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Earlier framing was wrong — the wall isn't 'Arch ARM clang 22 vs Arch x86_64 clang 23'. Arch x86_64 is also on 22.1.3; LLVM 23 isn't anywhere in extra/staging. The flags chromium 147 emits come from chromium's clang fork (Google maintains an LLVM fork with chromium-specific passes), not upstream LLVM 23. PKGBUILD'ing clang 23 is the wrong tree. Right tree: cross-compile from x86_64 so chromium's bundled clang prebuilt is reachable. CIPD has full linux-amd64 prebuilts, gclient sync works cleanly, no qemu-x86_64-static dance needed. his provisioned CT 220 chromium-builder-x86 on data (Ryzen 7 1700, 14 cores, 32 GiB RAM, 200 GiB ZFS). data is normally asleep — woke via /opt/herding/bin/wake-data. Reach pattern: hertz -> ssh data -> pct exec 220. Source fetch running as chromium-fetch.service transient unit on CT 220. Once src is in, plan: tools/clang/scripts/update.py for chromium's bundled clang + arm64 sysroot, gn gen with target_cpu=arm64, build, transfer aarch64 binary to ohm/fresnel/ampere. boltzmann chromium-builder LXD container preserved as fallback; can be torn down if cross-compile pans out. Co-Authored-By: Claude Opus 4.7 (1M context) --- arch/chromium-fourier/NEXT.md | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/arch/chromium-fourier/NEXT.md b/arch/chromium-fourier/NEXT.md index 9928e4ada8..39d5a1520d 100644 --- a/arch/chromium-fourier/NEXT.md +++ b/arch/chromium-fourier/NEXT.md @@ -108,3 +108,43 @@ essential. Discard the container and start over with option 2 if you pick that direction; otherwise iterate from current state. + +## Pivot 2026-04-26 — cross-compile from x86_64 + +After the analysis above, the framing shifted. The real wall isn't +"Arch ARM clang 22 vs LLVM 23" — Arch x86_64 is also on llvm 22.1.3, no +LLVM 23 anywhere in extra/staging. The flags chromium 147 emits +(`-fno-lifetime-dse`, `-fsanitize-ignore-for-ubsan-feature=array-bounds`, +the `/usr/lib/clang/23/...` path) come from **chromium's clang fork**, +not upstream LLVM 23. Chromium ships its own LLVM with chromium-specific +passes; the "23" in the path is chromium-internal versioning. + +Implication: PKGBUILD'ing clang 23 is the wrong tree. The right tree is +either pin to an older chromium (option 2 above) or **cross-compile from +an x86_64 host** so chromium's x86_64 bundled clang prebuilt is +reachable and `target_cpu="arm64"` produces the aarch64 binary cleanly. +Cross-compile sidesteps every wall we hit: + +- CIPD has full `linux-amd64` prebuilts (the gap was `linux-arm64`) +- Chromium's bundled clang downloads cleanly on x86_64 +- No qemu-x86_64-static dance for tools (host IS x86_64) +- `tools/clang/scripts/update.py` works as Google intends +- `gclient sync` works; no DEPS surgery needed + +his provisioned a cross-build host for this on 2026-04-26: + +- **CT 220 `chromium-builder-x86` on data**, x86_64 Ryzen 7 1700, + 14 cores, 32 GiB RAM + 8 GiB swap, 200 GiB ZFS rootfs. +- Reach via `mcp__hub-tools__remote_shell host=hertz` → + `ssh root@192.168.88.30` (data) → `pct exec 220 -- ...` +- `builder` user uid 1001, NOPASSWD sudo, `DisableSandbox` in pacman.conf. + +Source fetch started 2026-04-26 05:45 UTC as transient unit +`chromium-fetch.service` on CT 220. Estimated 1-2 h for `fetch +--no-history chromium` over the LAN. Then `tools/clang/scripts/update.py` +to install chromium's bundled clang (x86_64 host, arm64 sysroot), then +`gn gen` with `target_cpu="arm64"` + cross-compile flags, then build. + +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. From 8756ce38be8227b104589a3cb80d5f4efcc059d4 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Tue, 28 Apr 2026 12:02:18 +0000 Subject: [PATCH 06/88] chromium-fourier r2 + firefox-fourier 150.0.1 + KWIN_PIVOT.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- arch/chromium-fourier/KWIN_PIVOT.md | 181 ++++++++++++ arch/chromium-fourier/NEXT.md | 278 ++++++++++++++++++ arch/chromium-fourier/PKGBUILD | 175 ++++++++--- .../patches/chromeos-pipeline-bypass.patch | 22 -- .../patches/compiler-rt-adjust-paths.patch | 38 +++ .../patches/enable-v4l2-decoder-default.patch | 55 ++++ ...ternal-oes-on-modifier-external-only.patch | 187 ++++++++++++ .../wayland-allow-direct-egl-gles2.patch | 57 ++++ arch/firefox-fourier/PKGBUILD | 163 ++++++++++ arch/firefox-fourier/PLAN.md | 210 +++++++++++++ arch/firefox-fourier/mozconfig | 36 +++ .../0001-gfxinfo-v4l2-stateless-fourccs.patch | 75 +++++ .../0002-libwrapper-hwdevice-ctx-create.patch | 52 ++++ .../0003-ffmpegvideo-v4l2-request-route.patch | 208 +++++++++++++ .../patches/0004-prefs-v4l2-request.patch | 34 +++ 15 files changed, 1711 insertions(+), 60 deletions(-) create mode 100644 arch/chromium-fourier/KWIN_PIVOT.md delete mode 100644 arch/chromium-fourier/patches/chromeos-pipeline-bypass.patch create mode 100644 arch/chromium-fourier/patches/compiler-rt-adjust-paths.patch create mode 100644 arch/chromium-fourier/patches/enable-v4l2-decoder-default.patch create mode 100644 arch/chromium-fourier/patches/nv12-external-oes-on-modifier-external-only.patch create mode 100644 arch/chromium-fourier/patches/wayland-allow-direct-egl-gles2.patch create mode 100644 arch/firefox-fourier/PKGBUILD create mode 100644 arch/firefox-fourier/PLAN.md create mode 100644 arch/firefox-fourier/mozconfig create mode 100644 arch/firefox-fourier/patches/0001-gfxinfo-v4l2-stateless-fourccs.patch create mode 100644 arch/firefox-fourier/patches/0002-libwrapper-hwdevice-ctx-create.patch create mode 100644 arch/firefox-fourier/patches/0003-ffmpegvideo-v4l2-request-route.patch create mode 100644 arch/firefox-fourier/patches/0004-prefs-v4l2-request.patch diff --git a/arch/chromium-fourier/KWIN_PIVOT.md b/arch/chromium-fourier/KWIN_PIVOT.md new file mode 100644 index 0000000000..668442fefb --- /dev/null +++ b/arch/chromium-fourier/KWIN_PIVOT.md @@ -0,0 +1,181 @@ +# KWin pivot — fix the `glTexImage2D(GL_ALPHA)` stall + +## What we know + +KWin 6.6.4-1 on Arch Linux ARM (Plasma 6.6.4-1, mesa 26.0.5-1, libdrm +2.4.131-1) on ohm (PineTab2 / RK3566 / panfrost) silently corrupts its +GL command queue mid-frame whenever a wayland client posts a video +buffer. The journal carries a rolling stream of: + +``` +kwin_wayland: 0x4: GL_INVALID_VALUE in glTexImage2D(internalFormat=GL_ALPHA) +kwin_wayland: 0x4: GL_INVALID_OPERATION in glTexSubImage2D(invalid texture level 0) × N +``` + +`GL_ALPHA` is not a valid `internalFormat` for `glTexImage2D` under +**OpenGL ES 3.x** (it was the GLES1.x single-channel alpha format; +GLES3 deprecates it for sized formats — `GL_R8`, `GL_LUMINANCE8_ALPHA8`, +etc.). Once the texture allocation fails, the `glTexSubImage2D` calls +that should populate it all error at level 0. KWin keeps retrying the +same broken upload every frame, never recovers, and the present-callback +path that depends on that texture stops acking client frames. Every +wayland video client deadlocks on the missing ack. + +First occurrence in this box's journal: **2026-03-06** — the bug +predates any chromium-fourier work by roughly seven weeks. + +## Triangulation already in hand + +| Client | Outcome | +|---|---| +| chromium-fourier 149-r2 (with patch 3/3) | plays ~3 s @ 34.7 % CPU then renderer/GPU park in `futex_do_wait` | +| chromium-fourier 149-r2 (without patch 3/3) | plays ~10 s (slower path delays surfacing) then identical deadlock | +| VLC | `cannot convert decoder/filter output to any format supported by the output` → `could not initialize video chain` | +| mpv `--vo=null --hwdec=v4l2request` | `Could not create device.` (mpv-side bug, separate, unrelated) | +| ffmpeg `-hwaccel v4l2request -i bbb -f null -` | plays through clean at 36 fps; hardware path is healthy | + +Decode path is healthy on this hardware. The wall is exclusively the +compositor's GL backend. + +## Constraint: ohm is the only test box on hand + +ampere (RK3588 / panthor) is in the boxes-from-Shenzhen pile, currently +DOWN. fresnel (RK3399 / Pinebook Pro) is offline. boltzmann (Rock 5 +ITX+ build host) doesn't run KWin. We do every step on ohm; we accept +the wifi flakiness and the occasional reboot. + +## Phase 1 — Reproduce outside chrome and bound the trigger (1 evening) + +Goal: a deterministic, headless-or-near-headless reproduction that +doesn't require launching a 800-MB browser. + +1. **Smallest-possible client.** Build a 50-line C wayland client that + creates a `wp_linux_dmabuf_v1` buffer, pumps frames at 30 fps, and + exits when KWin first errors. Use `weston-simple-dmabuf-egl` from + the `weston` package as a starting template — already does exactly + this but without our specific format/modifier matrix. +2. **Vary the format/modifier matrix.** Run the smallest-possible + client with each of: NV12 + LINEAR, NV12 + AFBC, NV12 + AFRC, + AR24 + LINEAR, XR24 + LINEAR. We already know NV12 paths trigger; + confirming AR24/XR24 do *not* trigger localizes the bug to KWin's + YUV import path (vs a generic dmabuf import bug). +3. **Vary the buffer dimensions.** Some KWin texture-cache paths + allocate fixed-size internal scratch textures; non-power-of-two, + non-multiple-of-16, or specifically odd-aspect cases sometimes + trigger paths that healthy aspect ratios skip. Test 1920×1080, + 1280×720, 854×480, 640×360 and a deliberately weird 1366×768. +4. **Vary KWin scene type.** Switch + `kwin_wayland --scene-type=opengl` vs `--scene-type=opengl-es` + (current default on this hardware). If the bug only fires under + GLES, that's a strong signal — the offending site is in a + GLES-only fallback. + +By the end of Phase 1 we should have a one-line `weston-simple-dmabuf-egl +-format=NV12 -modifier=…` that triggers the GL_ALPHA error within +seconds, plus a yes/no answer to "does AR24 also trigger". + +## Phase 2 — Identify the call site (1–2 evenings) + +The crime scene is somewhere in `kwin/src/scene/*` or +`kwin/src/effects/*`. Suspects, ranked: + +- **`SurfaceItemWayland::createPixmapTexture` → `GLTexture::create` + with `GL_ALPHA`.** This is the most likely path: KWin allocates a + fallback per-plane texture when the dmabuf import path can't take + the buffer whole. NV12 has a Y plane (single-channel) and a CbCr + plane (two-channel); historically the Y plane has been allocated as + `GL_ALPHA` in software fallbacks. If the EGL dmabuf import returned + `EGL_BAD_ATTRIBUTE` for `external_only` modifiers and KWin fell + through to per-plane, this is exactly where it would land. +- **`BlurEffect::initBlurTexture` / `BackgroundContrastEffect::*`.** + Single-channel noise textures for blur dither. Less likely (these + fire on every frame regardless of video clients) but listed for + completeness. +- **Window-decoration text glyph cache.** Qt's QGLTexture historically + requested `GL_ALPHA` for monochrome glyph atlases. Plasma 6 should + have moved to `GL_RED` long ago, but a stale code path in a + third-party theme or systray icon could still hit it. +- **Cursor texture upload via `wl_shm_pool` + ARGB8888.** KWin's + cursor scene sometimes uploads via glTexImage2D — but the format + there is `GL_RGBA`, not `GL_ALPHA`. Probably not the suspect. + +Tooling to identify *which*: + +1. **`apitrace trace --api egl kwin_wayland …`** then + `apitrace dump trace.trace | grep -B5 GL_ALPHA`. Apitrace gives + us the C++ call stack at the offending site if KWin was built with + debug symbols. +2. **`MESA_GL_DEBUG=context KWIN_GL_DEBUG=1 kwin_wayland --replace`** + plus `glDebugMessageCallback` already installed in KWin's + `OpenGLBackend` will print the source/type/severity for each + `GL_INVALID_VALUE`. Whether the file/line in the message includes + the user-space caller depends on Mesa's debug-extension support; + on panfrost it usually does include the GL function name and an + ID, but not the C++ source — that is what apitrace adds. +3. **Build kwin from source** (`extra/kwin` PKGBUILD on Arch ARM, + patch in `-DDEBUG=ON`, `-DCMAKE_BUILD_TYPE=Debug`) so the call + stacks resolve to file:line. + +## Phase 3 — Write the patch (½ evening once Phase 2 is done) + +If the offender is a `GL_ALPHA` allocation in a GLES3 context, the +fix is mechanical: + +```diff +- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, +- GL_ALPHA, GL_UNSIGNED_BYTE, data); ++ glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, ++ GL_RED, GL_UNSIGNED_BYTE, data); +``` + +…and adjust the consuming shader's swizzle: + +```diff +- gl_FragColor = vec4(texture2D(s, uv).a, …); ++ gl_FragColor = vec4(texture2D(s, uv).r, …); +``` + +If the offender is a per-plane fallback in the dmabuf import path +(suspect #1 above), the patch is larger because the right fix is to +*not fall through to the broken path* — handle the `external_only` +case by binding `GL_TEXTURE_EXTERNAL_OES` instead. That mirrors the +chromium-fourier patch 3/3 done at the chromium layer; symmetry says +KWin should do the same in its `glTexImage` consumer. + +## Phase 4 — Ship and upstream (1 evening) + +1. **Local Arch package** as `kwin-fourier` under + `marfrit-packages/arch/kwin-fourier/`, sibling to chromium-fourier + and firefox-fourier. PKGBUILD inherits from `extra/kwin`, drops + in our patch, bumps `pkgrel`. Same `provides=kwin conflicts=kwin` + pattern. +2. **Validate on ohm** by running the chromium-fourier 149-r2 build + + the bbb sample for a minute uninterrupted. Success = no GL_ALPHA + in the journal, no stall, smooth playback at the 34.7 % CPU + number from the chromium validation. +3. **Upstream** via: + - File a `kwin` bug on bugs.kde.org with: apitrace fragment, our + hardware (Mali-G52 panfrost on RK3566 mainline), exact mesa + version, repro steps via `weston-simple-dmabuf-egl` if Phase 1 + produced one. + - Push an MR to invent.kde.org/plasma/kwin against `master`. +4. **Document** the fix in `chromium-fourier/docs/dmabuf-zero-copy.md` + so the next person who lands on the same wall finds the breadcrumb + trail. + +## What success looks like + +`chromium-fourier-149-r2` on ohm under KWin Wayland plays +`bbb_1080p30_h264.mp4` end-to-end at the 34.7 % CPU figure already +recorded by the architectural validation, with zero `GL_INVALID_VALUE` +in the journal during playback. That number is the goal of the entire +chromium-fourier campaign for RK3566 — it is currently blocked on a +bug that has nothing to do with chromium. + +## Scope discipline + +We do not turn this into "audit the entire KWin GLES backend." If +Phase 2 surfaces additional latent GL_INVALID_* errors that don't +matter for video playback, we note them in the bug report and move +on. The pivot is explicitly "remove this single wall so the +chromium-fourier patch series can ship a working stack on RK3566." diff --git a/arch/chromium-fourier/NEXT.md b/arch/chromium-fourier/NEXT.md index 39d5a1520d..44d8a4b03d 100644 --- a/arch/chromium-fourier/NEXT.md +++ b/arch/chromium-fourier/NEXT.md @@ -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