chromium-fourier r2 + firefox-fourier 150.0.1 + KWIN_PIVOT.md
build and publish packages / distcc-avahi-aarch64 (push) Successful in 46s
build and publish packages / lmcp-any (push) Successful in 9s
build and publish packages / lmcp-debian (push) Successful in 4s
build and publish packages / claude-his-any (push) Successful in 7s
build and publish packages / ffmpeg-v4l2-request-aarch64 (push) Successful in 12m8s
build and publish packages / claude-his-debian (push) Successful in 5s
build and publish packages / distcc-avahi-aarch64 (push) Successful in 46s
build and publish packages / lmcp-any (push) Successful in 9s
build and publish packages / lmcp-debian (push) Successful in 4s
build and publish packages / claude-his-any (push) Successful in 7s
build and publish packages / ffmpeg-v4l2-request-aarch64 (push) Successful in 12m8s
build and publish packages / claude-his-debian (push) Successful in 5s
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:
@@ -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."
|
||||||
@@ -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
|
The boltzmann `chromium-builder` LXD container is preserved as fallback
|
||||||
but no longer the active build host. If cross-compile pans out, that
|
but no longer the active build host. If cross-compile pans out, that
|
||||||
container can be torn down.
|
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 5–10× 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.
|
||||||
|
|||||||
+137
-38
@@ -4,19 +4,20 @@
|
|||||||
# (RK3566 hantro / RK3588 VDPU381) on **mainline** kernel + Wayland +
|
# (RK3566 hantro / RK3588 VDPU381) on **mainline** kernel + Wayland +
|
||||||
# panfrost / panthor — no vendor MPP, no Mali blob, no panfork, no
|
# panfrost / panthor — no vendor MPP, no Mali blob, no panfork, no
|
||||||
# 5.10 BSP kernel. Fills the niche that 7Ji's chromium-mpp explicitly
|
# 5.10 BSP kernel. Fills the niche that 7Ji's chromium-mpp explicitly
|
||||||
# does not (it forces BSP + X11 + vendor stack); see
|
# does not (it forces BSP + X11 + vendor stack); see STUDY.md and
|
||||||
# /home/mfritsche/src/marfrit-packages/arch/chromium-fourier/STUDY.md
|
# NEXT.md alongside this PKGBUILD for the full rationale and the
|
||||||
# for the full rationale.
|
# validation log on PineTab2 (RK3566).
|
||||||
#
|
#
|
||||||
# Build host: chromium-builder LXD container on boltzmann (8 cores,
|
# Multi-arch: builds natively on x86_64 and aarch64. The x86_64 path
|
||||||
# 28 GB RAM cap, 824 GB NVMe). 6-10 h initial build.
|
# is primarily a development / CI host; the runtime target audience is
|
||||||
|
# aarch64. The two patches are architecture-independent.
|
||||||
|
|
||||||
pkgname=chromium-fourier
|
pkgname=chromium-fourier
|
||||||
pkgver=147.0.7727.116
|
pkgver=147.0.7727.116
|
||||||
pkgrel=1
|
pkgrel=2
|
||||||
epoch=1
|
epoch=1
|
||||||
pkgdesc='Chromium with V4L2VDA HW video decode unlocked for mainline Linux Wayland on Rockchip'
|
pkgdesc='Chromium with V4L2VDA HW video decode unlocked for mainline Linux Wayland on Rockchip'
|
||||||
arch=('aarch64')
|
arch=('aarch64' 'x86_64')
|
||||||
url='https://www.chromium.org/Home'
|
url='https://www.chromium.org/Home'
|
||||||
license=('BSD-3-Clause')
|
license=('BSD-3-Clause')
|
||||||
depends=(
|
depends=(
|
||||||
@@ -68,45 +69,84 @@ optdepends=(
|
|||||||
provides=(chromium)
|
provides=(chromium)
|
||||||
conflicts=(chromium)
|
conflicts=(chromium)
|
||||||
options=('!lto' '!strip')
|
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
|
# Canonical chromium release tarball (5.7 GB compressed). Versions track
|
||||||
# PKGBUILD assumes /build/chromium/src is already populated.
|
# the chromium release schedule (see https://chromiumdash.appspot.com).
|
||||||
|
# When bumping pkgver the patches may need their hunk line numbers
|
||||||
|
# refreshed against the new tree — they are written against
|
||||||
|
# media/base/media_switches.cc and ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
|
||||||
|
# which both move around between minor releases.
|
||||||
source=(
|
source=(
|
||||||
'patches/chromeos-pipeline-bypass.patch'
|
"https://commondatastorage.googleapis.com/chromium-browser-official/chromium-${pkgver}.tar.xz"
|
||||||
|
'patches/enable-v4l2-decoder-default.patch'
|
||||||
|
'patches/wayland-allow-direct-egl-gles2.patch'
|
||||||
|
'patches/nv12-external-oes-on-modifier-external-only.patch'
|
||||||
)
|
)
|
||||||
sha256sums=(
|
sha256sums=(
|
||||||
'SKIP'
|
'SKIP'
|
||||||
|
'SKIP'
|
||||||
|
'SKIP'
|
||||||
|
'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() {
|
prepare() {
|
||||||
cd /build/chromium/src
|
cd "${srcdir}/chromium-${pkgver}"
|
||||||
|
|
||||||
# Fourier-local: bypass the chromeos pipeline so VaapiVideoDecoder /
|
# Fourier patch 1/2: flip kAcceleratedVideoDecodeLinux's default to
|
||||||
# V4L2VDA is reachable on Linux non-ChromeOS. The 7Ji-style gn args
|
# enabled when USE_V4L2_CODEC is the build's HW decode path. Without
|
||||||
# (use_v4l2_codec / use_v4lplugin / use_linux_v4l2_only) may be
|
# this the runtime master gate stays off on USE_V4L2_CODEC-only builds
|
||||||
# sufficient on their own; this patch is the fallback if they aren't.
|
# and chrome silently falls back to ffmpeg software decode. See the
|
||||||
patch -Np1 -i "${srcdir}/patches/chromeos-pipeline-bypass.patch" || true
|
# patch header for the validation log on RK3566 hantro.
|
||||||
|
patch -Np1 -i "${srcdir}/patches/enable-v4l2-decoder-default.patch"
|
||||||
|
|
||||||
|
# Fourier patch 2/3: re-allow the direct EGL/GLES2 path in the Wayland
|
||||||
|
# ozone surface factory so panfrost's EGL_EXT_image_dma_buf_import
|
||||||
|
# surfaces to chrome's GL display, lighting up the NV12 zero-copy
|
||||||
|
# native-pixmap pipeline. The launcher defaults to ANGLE (DCHECK in
|
||||||
|
# gl_context_egl.cc:241 fires on direct EGL with non-official builds);
|
||||||
|
# this patch keeps the direct path available for users who flip
|
||||||
|
# is_official_build=true and want the lower-CPU pipeline.
|
||||||
|
patch -Np1 -i "${srcdir}/patches/wayland-allow-direct-egl-gles2.patch"
|
||||||
|
|
||||||
|
# Fourier patch 3/3: pick GL_TEXTURE_EXTERNAL_OES for NV12 dmabufs
|
||||||
|
# whose DRM modifier is advertised external_only by the EGL driver.
|
||||||
|
# On panfrost / panthor every NV12 modifier (LINEAR + AFBC + AFRC) is
|
||||||
|
# external_only; chromium's default OzoneImageGLTexturesHolder picked
|
||||||
|
# GL_TEXTURE_2D and ANGLE then rejected the YUV EGLImage on a
|
||||||
|
# non-EXTERNAL_OES target, forcing the NV12->AR24 software conversion
|
||||||
|
# fallback. This closes that gap and enables the actual zero-copy
|
||||||
|
# path. Validated on ohm (RK3566 hantro): 1080p30 H.264 drops from
|
||||||
|
# ~131% combined CPU to ~34.7% (~3.8x). See patches/0004 for context.
|
||||||
|
patch -Np1 -i "${srcdir}/patches/nv12-external-oes-on-modifier-external-only.patch"
|
||||||
|
|
||||||
# Use system node, system java
|
# Use system node, system java
|
||||||
rm -f third_party/node/linux/node-linux-arm64/bin/node
|
case "$CARCH" in
|
||||||
mkdir -p third_party/node/linux/node-linux-arm64/bin
|
aarch64) _node_dir=node-linux-arm64 ;;
|
||||||
ln -sf /usr/bin/node third_party/node/linux/node-linux-arm64/bin/
|
x86_64) _node_dir=node-linux-x64 ;;
|
||||||
|
esac
|
||||||
|
rm -f "third_party/node/linux/${_node_dir}/bin/node"
|
||||||
|
mkdir -p "third_party/node/linux/${_node_dir}/bin"
|
||||||
|
ln -sf /usr/bin/node "third_party/node/linux/${_node_dir}/bin/"
|
||||||
ln -sf /usr/bin/java third_party/jdk/current/bin/ 2>/dev/null || true
|
ln -sf /usr/bin/java third_party/jdk/current/bin/ 2>/dev/null || true
|
||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd /build/chromium/src
|
cd "${srcdir}/chromium-${pkgver}"
|
||||||
|
|
||||||
|
case "$CARCH" in
|
||||||
|
aarch64) _target_cpu="arm64" ;;
|
||||||
|
x86_64) _target_cpu="x64" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
local _flags=(
|
local _flags=(
|
||||||
'is_official_build=true'
|
"target_cpu=\"${_target_cpu}\""
|
||||||
|
|
||||||
|
'is_official_build=false'
|
||||||
'is_debug=false'
|
'is_debug=false'
|
||||||
|
# dcheck_always_on defaults to !is_official_build (true here);
|
||||||
|
# explicitly off so the direct EGL/GLES2 path doesn't FATAL on
|
||||||
|
# gl_context_egl.cc:241's DCHECK(!global_texture_share_group_).
|
||||||
|
'dcheck_always_on=false'
|
||||||
'symbol_level=0'
|
'symbol_level=0'
|
||||||
'is_cfi=false'
|
'is_cfi=false'
|
||||||
'treat_warnings_as_errors=false'
|
'treat_warnings_as_errors=false'
|
||||||
@@ -123,7 +163,7 @@ build() {
|
|||||||
'use_v4l2_codec=true'
|
'use_v4l2_codec=true'
|
||||||
'use_v4lplugin=true'
|
'use_v4lplugin=true'
|
||||||
'use_linux_v4l2_only=true'
|
'use_linux_v4l2_only=true'
|
||||||
'use_vaapi=true'
|
'use_vaapi=false'
|
||||||
|
|
||||||
# Codec branding for proprietary codec support (H.264 etc.)
|
# Codec branding for proprietary codec support (H.264 etc.)
|
||||||
'ffmpeg_branding="Chrome"'
|
'ffmpeg_branding="Chrome"'
|
||||||
@@ -136,20 +176,38 @@ build() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
gn gen out/Default --args="${_flags[*]}"
|
gn gen out/Default --args="${_flags[*]}"
|
||||||
autoninja -C out/Default chrome chromedriver chrome_sandbox
|
ninja -C out/Default chrome chrome_crashpad_handler
|
||||||
}
|
}
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
cd /build/chromium/src
|
cd "${srcdir}/chromium-${pkgver}"
|
||||||
|
|
||||||
install -Dm755 out/Default/chrome "${pkgdir}/usr/lib/chromium/chromium"
|
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/chrome_crashpad_handler \
|
||||||
install -Dm755 out/Default/chromedriver "${pkgdir}/usr/bin/chromedriver"
|
"${pkgdir}/usr/lib/chromium/chrome_crashpad_handler"
|
||||||
|
[ -f out/Default/chrome_sandbox ] && install -Dm4755 out/Default/chrome_sandbox \
|
||||||
|
"${pkgdir}/usr/lib/chromium/chrome-sandbox"
|
||||||
|
[ -f out/Default/chromedriver ] && install -Dm755 out/Default/chromedriver \
|
||||||
|
"${pkgdir}/usr/bin/chromedriver"
|
||||||
|
|
||||||
|
# Bundled GL/Vulkan runtime — chrome dlopens these from its own dir,
|
||||||
|
# not /usr/lib/. Without them GL init fails and chrome falls back to
|
||||||
|
# software compositing.
|
||||||
|
for so in libEGL.so libGLESv2.so libvk_swiftshader.so libvulkan.so.1; do
|
||||||
|
[ -f "out/Default/$so" ] && install -Dm755 "out/Default/$so" \
|
||||||
|
"${pkgdir}/usr/lib/chromium/$so"
|
||||||
|
done
|
||||||
|
# ANGLE and SwiftShader ICD config files
|
||||||
|
for icd in out/Default/*_icd.json; do
|
||||||
|
[ -f "$icd" ] && install -Dm644 "$icd" \
|
||||||
|
"${pkgdir}/usr/lib/chromium/$(basename "$icd")"
|
||||||
|
done
|
||||||
|
|
||||||
# Resources / locales / pak files
|
# Resources / locales / pak files
|
||||||
for f in chrome_100_percent.pak chrome_200_percent.pak resources.pak \
|
for f in chrome_100_percent.pak chrome_200_percent.pak resources.pak \
|
||||||
v8_context_snapshot.bin icudtl.dat headless_lib_data.pak \
|
v8_context_snapshot.bin snapshot_blob.bin icudtl.dat \
|
||||||
headless_lib_strings.pak; do
|
headless_lib_data.pak headless_lib_strings.pak \
|
||||||
|
headless_command_resources.pak; do
|
||||||
[ -f "out/Default/$f" ] && install -Dm644 "out/Default/$f" \
|
[ -f "out/Default/$f" ] && install -Dm644 "out/Default/$f" \
|
||||||
"${pkgdir}/usr/lib/chromium/$f"
|
"${pkgdir}/usr/lib/chromium/$f"
|
||||||
done
|
done
|
||||||
@@ -160,7 +218,48 @@ package() {
|
|||||||
cp -r out/Default/locales/* "${pkgdir}/usr/lib/chromium/locales/"
|
cp -r out/Default/locales/* "${pkgdir}/usr/lib/chromium/locales/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Top-level launcher
|
# Launcher shim — defaults to ANGLE→GLES on Wayland with Vulkan
|
||||||
|
# disabled. Vulkan is off by default because:
|
||||||
|
# - panvk on RK3566 (Mali-G52 Bifrost) returns
|
||||||
|
# VK_ERROR_INCOMPATIBLE_DRIVER on chromium's probe and breaks
|
||||||
|
# V4L2 dispatch downstream (chrome falls back to FFmpeg software);
|
||||||
|
# - panthor on RK3588 (Mali-G610 Valhall) is more functional but
|
||||||
|
# not yet validated end-to-end against this build.
|
||||||
|
#
|
||||||
|
# User overrides for development on other Rockchips:
|
||||||
|
# --enable-features=Vulkan enable Vulkan (panthor / others)
|
||||||
|
# --use-vulkan=native|swiftshader pick the Vulkan backend
|
||||||
|
# --disable-features=Vulkan explicit re-disable
|
||||||
|
# Any of those on the command line short-circuits the launcher's
|
||||||
|
# default disable, so the user's intent always wins.
|
||||||
install -dm755 "${pkgdir}/usr/bin"
|
install -dm755 "${pkgdir}/usr/bin"
|
||||||
ln -s /usr/lib/chromium/chromium "${pkgdir}/usr/bin/chromium"
|
cat > "${pkgdir}/usr/bin/chromium" <<'LAUNCHER'
|
||||||
|
#!/bin/bash
|
||||||
|
# chromium-fourier launcher — V4L2 HW decode + Wayland + ANGLE
|
||||||
|
# Vulkan disabled by default; pass --enable-features=Vulkan or
|
||||||
|
# --use-vulkan=native to opt in (e.g. RK3588 panthor work).
|
||||||
|
|
||||||
|
USER_HANDLES_VULKAN=0
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--use-vulkan*|--enable-features=*Vulkan*|--disable-features=*Vulkan*|--use-angle=vulkan*)
|
||||||
|
USER_HANDLES_VULKAN=1
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
vulkan_default=()
|
||||||
|
if [ "$USER_HANDLES_VULKAN" = 0 ]; then
|
||||||
|
vulkan_default=(--disable-features=Vulkan)
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec /usr/lib/chromium/chromium \
|
||||||
|
--ozone-platform=wayland \
|
||||||
|
--use-gl=angle --use-angle=gles \
|
||||||
|
--enable-features=AcceleratedVideoDecoder \
|
||||||
|
"${vulkan_default[@]}" \
|
||||||
|
"$@"
|
||||||
|
LAUNCHER
|
||||||
|
chmod 0755 "${pkgdir}/usr/bin/chromium"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
From: Markus Fritsche <mfritsche@reauktion.de>
|
|
||||||
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
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
From: 7Ji <7Ji@example.com> (originally), adapted for chromium-fourier
|
||||||
|
Subject: Adjust compiler-rt library path layout for system clang on Arch
|
||||||
|
Linux ARM, where compiler-rt installs to lib/clang/N/lib/linux/ with
|
||||||
|
-aarch64 filename suffix instead of chromium's expected
|
||||||
|
lib/clang/N/lib/aarch64-unknown-linux-gnu/ layout.
|
||||||
|
|
||||||
|
diff --git a/build/config/clang/BUILD.gn b/build/config/clang/BUILD.gn
|
||||||
|
index d4de2e0cca0..57359c32121 100644
|
||||||
|
--- a/build/config/clang/BUILD.gn
|
||||||
|
+++ b/build/config/clang/BUILD.gn
|
||||||
|
@@ -130,12 +130,15 @@ template("clang_lib") {
|
||||||
|
} else if (is_linux || is_chromeos) {
|
||||||
|
if (current_cpu == "x64") {
|
||||||
|
_dir = "x86_64-unknown-linux-gnu"
|
||||||
|
+ _suffix = "-x86_64"
|
||||||
|
} else if (current_cpu == "x86") {
|
||||||
|
_dir = "i386-unknown-linux-gnu"
|
||||||
|
+ _suffix = "-i386"
|
||||||
|
} else if (current_cpu == "arm") {
|
||||||
|
_dir = "armv7-unknown-linux-gnueabihf"
|
||||||
|
} else if (current_cpu == "arm64") {
|
||||||
|
_dir = "aarch64-unknown-linux-gnu"
|
||||||
|
+ _suffix = "-aarch64"
|
||||||
|
} else {
|
||||||
|
assert(false) # Unhandled cpu type
|
||||||
|
}
|
||||||
|
@@ -166,6 +169,11 @@ template("clang_lib") {
|
||||||
|
assert(false) # Unhandled target platform
|
||||||
|
}
|
||||||
|
|
||||||
|
+ # Bit of a hack to make this find builtins from compiler-rt >= 16
|
||||||
|
+ if (is_linux || is_chromeos) {
|
||||||
|
+ _dir = "linux"
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
_clang_lib_dir = "$clang_base_path/lib/clang/$clang_version/lib"
|
||||||
|
_lib_file = "${_prefix}clang_rt.${_libname}${_suffix}.${_ext}"
|
||||||
|
libs = [ "$_clang_lib_dir/$_dir/$_lib_file" ]
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
Subject: media: default kAcceleratedVideoDecodeLinux to enabled when
|
||||||
|
USE_V4L2_CODEC is the build's hardware decode path
|
||||||
|
Date: 2026-04-26
|
||||||
|
|
||||||
|
Background
|
||||||
|
----------
|
||||||
|
chromium-fourier targets mainline-Linux Wayland on Rockchip (RK3566 hantro,
|
||||||
|
RK3588 VDPU381) where the only HW video decode path is V4L2 stateless
|
||||||
|
(via the in-tree media/gpu/v4l2 stack). The build is configured with
|
||||||
|
|
||||||
|
use_vaapi = false
|
||||||
|
use_v4l2_codec = true
|
||||||
|
use_v4lplugin = true
|
||||||
|
use_linux_v4l2_only = true
|
||||||
|
|
||||||
|
Without this patch, GPU-process V4L2 decode is compiled in but stays
|
||||||
|
runtime-disabled by default. The runtime master gate
|
||||||
|
`media::kAcceleratedVideoDecodeLinux` (the user-visible feature name is
|
||||||
|
"AcceleratedVideoDecoder") is currently flipped to ENABLED_BY_DEFAULT only
|
||||||
|
when `BUILDFLAG(USE_VAAPI)` is set. On a USE_V4L2_CODEC-only build the
|
||||||
|
feature stays DISABLED_BY_DEFAULT, the linux gpu_mojo_media_client returns
|
||||||
|
`VideoDecoderType::kUnknown`, and `<video>` falls all the way back to
|
||||||
|
`media/filters/ffmpeg_video_decoder.cc` (software).
|
||||||
|
|
||||||
|
We confirmed this by hand on the PineTab2 (RK3566 hantro): with
|
||||||
|
`--enable-features=AcceleratedVideoDecoder` chrome correctly selects
|
||||||
|
`V4L2VideoDecoder` for h264 main, opens /dev/video1 + /dev/media0,
|
||||||
|
allocates 17 OUTPUT + 6 CAPTURE NV12 buffers, and runs SetExtCtrlsInit for
|
||||||
|
H264. Without the runtime flag, none of that happens.
|
||||||
|
|
||||||
|
Fix
|
||||||
|
---
|
||||||
|
Treat `USE_V4L2_CODEC` symmetrically with `USE_VAAPI` for the runtime
|
||||||
|
default of the master gate. A user can still disable it via
|
||||||
|
`--disable-features=AcceleratedVideoDecoder`.
|
||||||
|
|
||||||
|
This does NOT touch the `kAcceleratedVideoDecodeLinuxGL` companion gate
|
||||||
|
(already ENABLED_BY_DEFAULT) or any of the per-decoder selection logic in
|
||||||
|
`media/mojo/services/gpu_mojo_media_client_linux.cc` -- that file already
|
||||||
|
dispatches to the V4L2 decoder when `USE_V4L2_CODEC && !USE_VAAPI`, gated
|
||||||
|
behind the master flag we are flipping here.
|
||||||
|
|
||||||
|
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
|
||||||
|
--- a/media/base/media_switches.cc
|
||||||
|
+++ b/media/base/media_switches.cc
|
||||||
|
@@ -749,7 +749,7 @@ BASE_FEATURE(kUnifiedAutoplay, base::FEATURE_ENABLED_BY_DEFAULT);
|
||||||
|
// on chromeos, but needs an experiment on linux.
|
||||||
|
BASE_FEATURE(kAcceleratedVideoDecodeLinux,
|
||||||
|
"AcceleratedVideoDecoder",
|
||||||
|
-#if BUILDFLAG(USE_VAAPI)
|
||||||
|
+#if BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC)
|
||||||
|
base::FEATURE_ENABLED_BY_DEFAULT);
|
||||||
|
#else
|
||||||
|
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
Subject: [PATCH] gpu/ozone: pick GL_TEXTURE_EXTERNAL_OES for NV12 dmabufs whose
|
||||||
|
DRM modifier is advertised external_only by the EGL driver
|
||||||
|
Date: 2026-04-28
|
||||||
|
|
||||||
|
Background
|
||||||
|
----------
|
||||||
|
On mainline-Linux Mali GPUs (mesa panfrost / panthor on Bifrost / Valhall)
|
||||||
|
every NV12 modifier exposed by `eglQueryDmaBufModifiersEXT` is flagged
|
||||||
|
`external_only` — DRM_FORMAT_MOD_LINEAR + ARM AFBC × 2 + ARM AFRC. Mesa's
|
||||||
|
behavior is spec-correct: GLES sampling of multi-plane formats is
|
||||||
|
defined only via `samplerExternalOES`, never `sampler2D`. The chromium
|
||||||
|
NV12 import path at
|
||||||
|
`gpu/command_buffer/service/shared_image/ozone_image_gl_textures_holder.cc`
|
||||||
|
already chooses `GL_TEXTURE_EXTERNAL_OES` when the SharedImageFormat is
|
||||||
|
flagged `PrefersExternalSampler` — but that flag is only set for the
|
||||||
|
generic "multi-plane on Linux" case in
|
||||||
|
`media/gpu/chromeos/mailbox_video_frame_converter.cc`. Frames that
|
||||||
|
arrive with an `external_only`-flagged modifier from a producer that
|
||||||
|
didn't set the flag (V4L2 hantro NV12 with AFBC/AFRC capture format on
|
||||||
|
RK3588's rkvdec2, future NativePixmap producers, etc.) hit the
|
||||||
|
`GL_TEXTURE_2D` path; ANGLE's `validationES.cpp:4894` then rejects YUV
|
||||||
|
EGLImages on non-EXTERNAL_OES targets, and the import fails.
|
||||||
|
|
||||||
|
This patch closes the gap: the texture-target choice in
|
||||||
|
`OzoneImageGLTexturesHolder::GetBinding` now consults the EGL driver's
|
||||||
|
`external_only` annotation for the pixmap's actual modifier in addition
|
||||||
|
to `format.PrefersExternalSampler()`. If either says "external sampler
|
||||||
|
required", the target switches to `GL_TEXTURE_EXTERNAL_OES`. Skia
|
||||||
|
Ganesh handles `GL_TEXTURE_EXTERNAL_OES` natively via
|
||||||
|
`GrGLTextureInfo.fTarget`, so no shader changes are required. Same
|
||||||
|
infrastructure chromium already uses for Android camera / decoder
|
||||||
|
dmabufs, retargeted at the Linux ozone layer.
|
||||||
|
|
||||||
|
Result is cached per `(fourcc, modifier)` tuple via a function-local
|
||||||
|
static `base::flat_map`, so the EGL query is not on the per-frame hot
|
||||||
|
path — once per unique format+modifier combination, after which the
|
||||||
|
runtime cost is a hash lookup behind a base::Lock.
|
||||||
|
|
||||||
|
Bug crbug.com/1498703 is the closest existing tracker; framing this
|
||||||
|
upstream as "make Linux NV12 import path consistent with the
|
||||||
|
ChromeOS PrefersExternalSampler default" is the right angle.
|
||||||
|
|
||||||
|
diff --git a/gpu/command_buffer/service/shared_image/ozone_image_gl_textures_holder.cc b/gpu/command_buffer/service/shared_image/ozone_image_gl_textures_holder.cc
|
||||||
|
index 525bdcb0dc..43b0723326 100644
|
||||||
|
--- a/gpu/command_buffer/service/shared_image/ozone_image_gl_textures_holder.cc
|
||||||
|
+++ b/gpu/command_buffer/service/shared_image/ozone_image_gl_textures_holder.cc
|
||||||
|
@@ -16,6 +16,7 @@
|
||||||
|
#include "ui/gl/gl_bindings.h"
|
||||||
|
#include "ui/gl/scoped_binders.h"
|
||||||
|
#include "ui/ozone/public/gl_ozone.h"
|
||||||
|
+#include "ui/ozone/common/native_pixmap_egl_binding.h"
|
||||||
|
#include "ui/ozone/public/native_pixmap_gl_binding.h"
|
||||||
|
#include "ui/ozone/public/ozone_platform.h"
|
||||||
|
#include "ui/ozone/public/surface_factory_ozone.h"
|
||||||
|
@@ -82,7 +83,14 @@ std::unique_ptr<ui::NativePixmapGLBinding> GetBinding(
|
||||||
|
// being multiplanar (if using per-plane sampling of a multiplanar texture,
|
||||||
|
// the buffer format passed in here must be the single-planar format of the
|
||||||
|
// plane).
|
||||||
|
- if (format.PrefersExternalSampler()) {
|
||||||
|
+ // chromium-fourier: also pick GL_TEXTURE_EXTERNAL_OES whenever the
|
||||||
|
+ // pixmap's DRM modifier is advertised external_only by the EGL
|
||||||
|
+ // driver. Mesa panfrost / panthor mark every NV12 modifier
|
||||||
|
+ // external_only — the PrefersExternalSampler flag alone misses
|
||||||
|
+ // the AFBC / AFRC tiled paths.
|
||||||
|
+ if (format.PrefersExternalSampler() ||
|
||||||
|
+ ui::NativePixmapEGLBinding::ModifierRequiresExternalOES(
|
||||||
|
+ pixmap.get(), plane_format)) {
|
||||||
|
target = GL_TEXTURE_EXTERNAL_OES;
|
||||||
|
} else {
|
||||||
|
target = GL_TEXTURE_2D;
|
||||||
|
diff --git a/ui/ozone/common/native_pixmap_egl_binding.cc b/ui/ozone/common/native_pixmap_egl_binding.cc
|
||||||
|
index 31877f4459..6855c1093e 100644
|
||||||
|
--- a/ui/ozone/common/native_pixmap_egl_binding.cc
|
||||||
|
+++ b/ui/ozone/common/native_pixmap_egl_binding.cc
|
||||||
|
@@ -6,10 +6,13 @@
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
+#include "base/containers/flat_map.h"
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/memory/scoped_refptr.h"
|
||||||
|
+#include "base/no_destructor.h"
|
||||||
|
#include "base/notreached.h"
|
||||||
|
#include "base/numerics/safe_conversions.h"
|
||||||
|
+#include "base/synchronization/lock.h"
|
||||||
|
#include "ui/gfx/linux/drm_util_linux.h"
|
||||||
|
#include "ui/gl/gl_bindings.h"
|
||||||
|
#include "ui/gl/gl_surface_egl.h"
|
||||||
|
@@ -56,6 +59,75 @@ bool NativePixmapEGLBinding::IsSharedImageFormatSupported(
|
||||||
|
viz::SharedImageFormat format) {
|
||||||
|
return GetFourCCFormatFromSharedImageFormat(format) != DRM_FORMAT_INVALID;
|
||||||
|
}
|
||||||
|
+// static
|
||||||
|
+bool NativePixmapEGLBinding::ModifierRequiresExternalOES(
|
||||||
|
+ const gfx::NativePixmap* pixmap,
|
||||||
|
+ viz::SharedImageFormat format) {
|
||||||
|
+ // chromium-fourier: query the EGL driver for the (fourcc, modifier)
|
||||||
|
+ // tuple's external_only flag. Cache results — eglQueryDmaBufModifiersEXT
|
||||||
|
+ // is a synchronous round-trip into the driver and we want it off the
|
||||||
|
+ // per-frame hot path. The cache lives for the lifetime of the GPU
|
||||||
|
+ // process (modifier tables don't change after EGL init).
|
||||||
|
+ if (!pixmap) {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ const uint64_t modifier = pixmap->GetFormatModifier();
|
||||||
|
+ if (modifier == gfx::NativePixmapHandle::kNoModifier) {
|
||||||
|
+ // Implicit linear — same answer the driver would give for the
|
||||||
|
+ // matching LINEAR entry, but cheaper not to query.
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ const uint32_t fourcc = GetFourCCFormatFromSharedImageFormat(format);
|
||||||
|
+ if (fourcc == DRM_FORMAT_INVALID) {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ using Key = std::pair<uint32_t, uint64_t>;
|
||||||
|
+ static base::NoDestructor<base::Lock> cache_lock;
|
||||||
|
+ static base::NoDestructor<base::flat_map<Key, bool>> cache;
|
||||||
|
+ {
|
||||||
|
+ base::AutoLock lock(*cache_lock);
|
||||||
|
+ auto it = cache->find({fourcc, modifier});
|
||||||
|
+ if (it != cache->end()) {
|
||||||
|
+ return it->second;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ bool external_only = false;
|
||||||
|
+ do {
|
||||||
|
+ auto* display = gl::GLSurfaceEGL::GetGLDisplayEGL();
|
||||||
|
+ if (!display || !display->ext->b_EGL_EXT_image_dma_buf_import_modifiers) {
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ EGLDisplay egl_display = display->GetDisplay();
|
||||||
|
+ EGLint num_modifiers = 0;
|
||||||
|
+ if (!eglQueryDmaBufModifiersEXT(egl_display, fourcc, 0, nullptr, nullptr,
|
||||||
|
+ &num_modifiers) ||
|
||||||
|
+ num_modifiers <= 0) {
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ std::vector<EGLuint64KHR> modifiers(num_modifiers);
|
||||||
|
+ std::vector<EGLBoolean> ext_only(num_modifiers);
|
||||||
|
+ if (!eglQueryDmaBufModifiersEXT(egl_display, fourcc, num_modifiers,
|
||||||
|
+ modifiers.data(), ext_only.data(),
|
||||||
|
+ &num_modifiers)) {
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ for (EGLint i = 0; i < num_modifiers; ++i) {
|
||||||
|
+ if (modifiers[i] == modifier) {
|
||||||
|
+ external_only = (ext_only[i] == EGL_TRUE);
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ } while (0);
|
||||||
|
+
|
||||||
|
+ {
|
||||||
|
+ base::AutoLock lock(*cache_lock);
|
||||||
|
+ cache->insert_or_assign({fourcc, modifier}, external_only);
|
||||||
|
+ }
|
||||||
|
+ return external_only;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
|
||||||
|
// static
|
||||||
|
std::unique_ptr<NativePixmapGLBinding> NativePixmapEGLBinding::Create(
|
||||||
|
diff --git a/ui/ozone/common/native_pixmap_egl_binding.h b/ui/ozone/common/native_pixmap_egl_binding.h
|
||||||
|
index 61fb0de77f..ad3ac9ced5 100644
|
||||||
|
--- a/ui/ozone/common/native_pixmap_egl_binding.h
|
||||||
|
+++ b/ui/ozone/common/native_pixmap_egl_binding.h
|
||||||
|
@@ -27,6 +27,17 @@ class NativePixmapEGLBinding : public NativePixmapGLBinding {
|
||||||
|
|
||||||
|
static bool IsSharedImageFormatSupported(viz::SharedImageFormat format);
|
||||||
|
|
||||||
|
+ // chromium-fourier: returns true when |pixmap|'s DRM format modifier
|
||||||
|
+ // is advertised by the EGL driver as `external_only` for the given
|
||||||
|
+ // SharedImage format. Used at SharedImage creation time to override
|
||||||
|
+ // the default GL_TEXTURE_2D target to GL_TEXTURE_EXTERNAL_OES so that
|
||||||
|
+ // mesa panfrost / panthor NV12 dmabufs (always external_only) import
|
||||||
|
+ // cleanly via glEGLImageTargetTexture2DOES + samplerExternalOES.
|
||||||
|
+ // Result is cached per (fourcc, modifier) tuple — the underlying
|
||||||
|
+ // eglQueryDmaBufModifiersEXT call is not on the per-frame hot path.
|
||||||
|
+ static bool ModifierRequiresExternalOES(const gfx::NativePixmap* pixmap,
|
||||||
|
+ viz::SharedImageFormat format);
|
||||||
|
+
|
||||||
|
// Create an EGLImage from a given NativePixmap and plane and bind
|
||||||
|
// |texture_id| to |target| followed by binding the image to |target|. The
|
||||||
|
// color space is for the external sampler: When we sample the YUV buffer as
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
Subject: ozone/wayland: re-allow direct EGL/GLES2 path (no ANGLE shim)
|
||||||
|
Date: 2026-04-26
|
||||||
|
|
||||||
|
Background
|
||||||
|
----------
|
||||||
|
On Wayland-only ozone builds the surface factory currently advertises only
|
||||||
|
ANGLE-mediated GL implementations (`kOpenGL`, `kOpenGLES`, `kSwiftShader`,
|
||||||
|
`kVulkan`). Anything driving `--use-gl=egl` (the historical
|
||||||
|
`kGLImplementationEGLGLES2`) is rejected at startup with
|
||||||
|
|
||||||
|
Requested GL implementation (gl=egl-gles2,angle=none) not found in
|
||||||
|
allowed implementations: [(gl=egl-angle,angle=opengl|opengles|...)]
|
||||||
|
|
||||||
|
The downstream switch already handles `kGLImplementationEGLGLES2` in
|
||||||
|
`GetGLOzone`, so the dispatcher is wired -- it's the *advertisement* that
|
||||||
|
got tightened.
|
||||||
|
|
||||||
|
The cost of that tightening on RK3566 hantro / panfrost is real: with
|
||||||
|
ANGLE in the path, chrome's display-side EGL is ANGLE's own EGL. ANGLE's
|
||||||
|
GLES backend on Linux does not propagate
|
||||||
|
`EGL_EXT_image_dma_buf_import` through to the chrome GL display, so
|
||||||
|
`gpu_feature_info.supports_nv12_gl_native_pixmap` ends up false. That in
|
||||||
|
turn forces the V4L2-decoded NV12 frames through the
|
||||||
|
NV12-to-AR24 VPP conversion path before they hit the compositor, costing
|
||||||
|
~85 % CPU at 1080p30 even though the hantro VPU is doing the actual
|
||||||
|
decode for free.
|
||||||
|
|
||||||
|
Allowing the direct EGL/GLES2 path back means chrome's EGL is panfrost's
|
||||||
|
EGL (via mesa), which exposes the dmabuf-import extensions natively, and
|
||||||
|
the zero-copy NV12 native pixmap path lights up.
|
||||||
|
|
||||||
|
Fix
|
||||||
|
---
|
||||||
|
Add `kGLImplementationEGLGLES2` to the head of the allowed list; ANGLE
|
||||||
|
remains the default fallback and is still selected when the user passes
|
||||||
|
`--use-gl=angle ...`. The position is deliberate: on a USE_V4L2_CODEC
|
||||||
|
hardware-decode build the user almost always wants the dmabuf-capable
|
||||||
|
direct path; ANGLE is still there for browsers that need its conformance
|
||||||
|
fixups.
|
||||||
|
|
||||||
|
This does not affect non-Wayland ozone backends.
|
||||||
|
|
||||||
|
diff --git a/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc b/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
|
||||||
|
--- a/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
|
||||||
|
+++ b/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
|
||||||
|
@@ -223,6 +223,10 @@ std::vector<gl::GLImplementationParts>
|
||||||
|
WaylandSurfaceFactory::GetAllowedGLImplementations() {
|
||||||
|
std::vector<gl::GLImplementationParts> impls;
|
||||||
|
if (egl_implementation_) {
|
||||||
|
+ // chromium-fourier: keep the direct EGL/GLES2 path available so
|
||||||
|
+ // panfrost's EGL_EXT_image_dma_buf_import surfaces to chrome's GL
|
||||||
|
+ // display layer. See patch header for rationale.
|
||||||
|
+ impls.emplace_back(gl::GLImplementationParts(gl::kGLImplementationEGLGLES2));
|
||||||
|
impls.emplace_back(gl::ANGLEImplementation::kOpenGL);
|
||||||
|
impls.emplace_back(gl::ANGLEImplementation::kOpenGLES);
|
||||||
|
impls.emplace_back(gl::ANGLEImplementation::kSwiftShader);
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
# Maintainer: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
#
|
||||||
|
# Firefox with V4L2 stateless (request API) hardware video decode
|
||||||
|
# unlocked for mainline-Linux Rockchip (RK3399 rkvdec, RK3566/RK3588
|
||||||
|
# hantro multiplanar, RK3588 rkvdec2). Sibling to chromium-fourier;
|
||||||
|
# same niche. No vendor MPP, no Mali blob, no panfork, no 5.10 BSP.
|
||||||
|
#
|
||||||
|
# Patch series adds 4 thin shims around upstream firefox (~+169 lines,
|
||||||
|
# zero deletions). Architecture: stateless decode rides libavcodec's
|
||||||
|
# v4l2_request hwaccel (AV_HWDEVICE_TYPE_DRM); no separate Mozilla V4L2
|
||||||
|
# decoder gets written. See ../../arch/firefox-fourier/PLAN.md for
|
||||||
|
# the full diagnosis. Mozilla bug 1969297.
|
||||||
|
|
||||||
|
pkgname=firefox-fourier
|
||||||
|
pkgver=150.0.1
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc='Firefox with V4L2 stateless HW video decode unlocked for mainline Linux Rockchip'
|
||||||
|
arch=('aarch64' 'x86_64')
|
||||||
|
url='https://www.mozilla.org/firefox'
|
||||||
|
license=('MPL-2.0')
|
||||||
|
depends=(
|
||||||
|
alsa-lib
|
||||||
|
at-spi2-core
|
||||||
|
cairo
|
||||||
|
dbus
|
||||||
|
ffmpeg
|
||||||
|
fontconfig
|
||||||
|
freetype2
|
||||||
|
gcc-libs
|
||||||
|
gdk-pixbuf2
|
||||||
|
glib2
|
||||||
|
glibc
|
||||||
|
gtk3
|
||||||
|
hicolor-icon-theme
|
||||||
|
libdrm
|
||||||
|
libpulse
|
||||||
|
libva
|
||||||
|
libxcb
|
||||||
|
libxkbcommon
|
||||||
|
mesa
|
||||||
|
nspr
|
||||||
|
nss
|
||||||
|
pango
|
||||||
|
pciutils
|
||||||
|
ttf-liberation
|
||||||
|
v4l-utils
|
||||||
|
)
|
||||||
|
makedepends=(
|
||||||
|
cbindgen
|
||||||
|
clang
|
||||||
|
imagemagick
|
||||||
|
inetutils
|
||||||
|
lld
|
||||||
|
llvm
|
||||||
|
mesa
|
||||||
|
nasm
|
||||||
|
nodejs
|
||||||
|
python
|
||||||
|
rust
|
||||||
|
unzip
|
||||||
|
wasi-compiler-rt
|
||||||
|
wasi-libc
|
||||||
|
yasm
|
||||||
|
zip
|
||||||
|
)
|
||||||
|
optdepends=(
|
||||||
|
'hunspell-en_us: spell checking, American English'
|
||||||
|
'libnotify: send notifications when downloads complete'
|
||||||
|
'pulseaudio: audio support'
|
||||||
|
)
|
||||||
|
provides=(firefox)
|
||||||
|
conflicts=(firefox)
|
||||||
|
options=('!emptydirs' '!strip')
|
||||||
|
source=(
|
||||||
|
"https://archive.mozilla.org/pub/firefox/releases/${pkgver}/source/firefox-${pkgver}.source.tar.xz"
|
||||||
|
'mozconfig'
|
||||||
|
# Arch's official firefox patches — toolchain glue for clang 22 +
|
||||||
|
# glibc 2.43 + Rust 1.95+. Picked up verbatim because we hit the same
|
||||||
|
# walls. arch-0001 (install-under-remoting) skipped — our launcher
|
||||||
|
# ships under /usr/bin/firefox-fourier with our own wrapper.
|
||||||
|
# https://gitlab.archlinux.org/archlinux/packaging/packages/firefox
|
||||||
|
'arch-0002-Bug-2033279-Make-enable-rust-simd-work-with-Rust-1.9.patch'
|
||||||
|
'arch-0003-Patch-glsl-optimizer-to-build-with-glibc-2.43.patch'
|
||||||
|
'arch-0004-Bug-2023597-Use-wasm32-wasip1-target-for-clang-22.1-.patch'
|
||||||
|
# firefox-fourier patches — V4L2 stateless decode unlock.
|
||||||
|
'0001-gfxinfo-v4l2-stateless-fourccs.patch'
|
||||||
|
'0002-libwrapper-hwdevice-ctx-create.patch'
|
||||||
|
'0003-ffmpegvideo-v4l2-request-route.patch'
|
||||||
|
'0004-prefs-v4l2-request.patch'
|
||||||
|
)
|
||||||
|
sha256sums=('SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP')
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
cd "${srcdir}/firefox-${pkgver}"
|
||||||
|
|
||||||
|
# Toolchain glue (Arch upstream) — apply BEFORE the fourier patches.
|
||||||
|
patch -Np1 -i "${srcdir}/arch-0002-Bug-2033279-Make-enable-rust-simd-work-with-Rust-1.9.patch"
|
||||||
|
patch -Np1 -i "${srcdir}/arch-0003-Patch-glsl-optimizer-to-build-with-glibc-2.43.patch"
|
||||||
|
patch -Np1 -i "${srcdir}/arch-0004-Bug-2023597-Use-wasm32-wasip1-target-for-clang-22.1-.patch"
|
||||||
|
|
||||||
|
# Fourier patches — order matters; see ../PLAN.md for rationale.
|
||||||
|
patch -Np1 -i "${srcdir}/0001-gfxinfo-v4l2-stateless-fourccs.patch"
|
||||||
|
patch -Np1 -i "${srcdir}/0002-libwrapper-hwdevice-ctx-create.patch"
|
||||||
|
patch -Np1 -i "${srcdir}/0003-ffmpegvideo-v4l2-request-route.patch"
|
||||||
|
patch -Np1 -i "${srcdir}/0004-prefs-v4l2-request.patch"
|
||||||
|
|
||||||
|
cp "${srcdir}/mozconfig" .mozconfig
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cd "${srcdir}/firefox-${pkgver}"
|
||||||
|
|
||||||
|
# Arch's makepkg.conf injects -fexceptions into CFLAGS/CXXFLAGS by
|
||||||
|
# default for hardening. Mozilla's STL wrappers refuse to compile
|
||||||
|
# with exceptions enabled (#error "STL code can only be used with
|
||||||
|
# -fno-exceptions"). Strip the offender before mach configure picks
|
||||||
|
# up the env. Same trick the upstream Arch firefox PKGBUILD uses.
|
||||||
|
CFLAGS="${CFLAGS//-fexceptions/}"
|
||||||
|
CXXFLAGS="${CXXFLAGS//-fexceptions/}"
|
||||||
|
export CFLAGS CXXFLAGS
|
||||||
|
|
||||||
|
export MOZ_NOSPAM=1
|
||||||
|
export MOZ_API_KEY_UNUSED=1
|
||||||
|
export MOZ_TELEMETRY_REPORTING=
|
||||||
|
export MOZ_REQUIRE_SIGNING=
|
||||||
|
export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
|
||||||
|
export PYTHON=/usr/bin/python
|
||||||
|
|
||||||
|
./mach configure
|
||||||
|
./mach build
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "${srcdir}/firefox-${pkgver}"
|
||||||
|
|
||||||
|
DESTDIR="${pkgdir}" ./mach install
|
||||||
|
|
||||||
|
# Move mach's default /usr/local/* layout to /usr/* so we conflict
|
||||||
|
# with `firefox` cleanly and `provides=firefox` actually works.
|
||||||
|
# `cp -r` preserves the bin symlink (target lives in /usr/local) —
|
||||||
|
# delete it before staging the launcher so `cat >` doesn't follow a
|
||||||
|
# dangling symlink and ENOENT.
|
||||||
|
if [ -d "${pkgdir}/usr/local" ]; then
|
||||||
|
cp -r "${pkgdir}/usr/local/." "${pkgdir}/usr/"
|
||||||
|
rm -rf "${pkgdir}/usr/local"
|
||||||
|
fi
|
||||||
|
rm -f "${pkgdir}/usr/bin/firefox-fourier"
|
||||||
|
|
||||||
|
# Launcher script. mach's install drops the binary at
|
||||||
|
# /usr/lib/firefox-fourier/firefox-fourier (a small bash launcher) plus
|
||||||
|
# firefox-fourier-bin alongside; we exec the launcher.
|
||||||
|
cat > "${pkgdir}/usr/bin/firefox-fourier" <<'LAUNCHER'
|
||||||
|
#!/bin/bash
|
||||||
|
# firefox-fourier launcher — V4L2 stateless HW decode path defaults.
|
||||||
|
# Patch 4/4 already defaults media.ffmpeg.v4l2-request.enabled=true on
|
||||||
|
# Linux; the env vars below cover the platform-detection bits firefox
|
||||||
|
# still consults at startup.
|
||||||
|
export MOZ_ENABLE_WAYLAND="${MOZ_ENABLE_WAYLAND:-1}"
|
||||||
|
export MOZ_X11_EGL="${MOZ_X11_EGL:-1}"
|
||||||
|
exec /usr/lib/firefox-fourier/firefox-fourier "$@"
|
||||||
|
LAUNCHER
|
||||||
|
chmod 0755 "${pkgdir}/usr/bin/firefox-fourier"
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
# firefox-fourier — V4L2 Stateless Decoder Patch Plan
|
||||||
|
|
||||||
|
Plan to extend Firefox 149's V4L2 hardware decode path to cover Rockchip
|
||||||
|
mainline kernel boards (RK3399 rkvdec, RK3566/RK3588 hantro, RK3588
|
||||||
|
rkvdec2) by routing stateless `S264`/`S265`/`VP9F` fourccs through
|
||||||
|
libavcodec's `v4l2_request` hwaccel, which mainline FFmpeg surfaces via
|
||||||
|
`AV_HWDEVICE_TYPE_DRM` (no dedicated `_V4L2REQUEST` enum exists upstream
|
||||||
|
— confirmed against `libavutil/hwcontext.h`).
|
||||||
|
|
||||||
|
## 1. Files touched (in order)
|
||||||
|
|
||||||
|
| # | Path | Change | Lines |
|
||||||
|
|---|------|--------|-------|
|
||||||
|
| 1 | `widget/gtk/GfxInfo.cpp` | `V4L2ProbeDevice` (~L1030–1110): add `S264`/`S265`/`VP9F` matches alongside existing `H264`/`HEVC`/`VP90`. Set `mIsV4L2Supported = Some(true)` and OR the same `CODEC_HW_DEC_*` bits. Tag a new bool `mV4L2IsStateless` so downstream can branch. | +35 / -2 |
|
||||||
|
| 2 | `dom/media/platforms/ffmpeg/FFmpegLibWrapper.h` | Add wrappers for `av_hwdevice_ctx_create` (currently only `_alloc`/`_init` per L173–174) and `av_hwdevice_find_type_by_name`. Needed because stateless wants the *device-path-aware* `_create` form to bind `/dev/dri/renderD128`. | +4 / 0 |
|
||||||
|
| 3 | `dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp` | `dlsym` the two new pointers; gate behind `LIBAVUTIL_VERSION_MAJOR >= 56`. | +6 / 0 |
|
||||||
|
| 4 | `dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h` | Add `AVBufferRef* mDRMDeviceContext = nullptr;` and `bool mUsingV4L2Request = false;` next to existing `mUsingV4L2`. | +3 / 0 |
|
||||||
|
| 5 | `dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp` | New `CreateV4L2RequestDeviceContext()` modelled on `CreateVAAPIDeviceContext` (current uses `av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI)`). Wire it in alongside the existing V4L2 init branch (the one feeding `ChooseV4L2PixelFormat` at L~258). `FindVideoHardwareAVCodec` call at L708 stays unchanged for stateless — we want the *generic* `h264`/`hevc`/`vp9` decoder + a DRM hw_device_ctx, not a `_v4l2m2m` codec. | +90 / -5 |
|
||||||
|
| 6 | `modules/libpref/init/StaticPrefList.yaml` | New pref `media.ffmpeg.v4l2-request.enabled` mirroring `media.ffmpeg.vaapi.enabled` (default `@IS_LINUX@`). | +6 / 0 |
|
||||||
|
| 7 | `dom/media/ipc/RDDProcessHost.cpp` + sandbox policy file | Whitelist `/dev/media*`, `/dev/dri/renderD*`, `/dev/video*` for the RDD process (already partly done for VAAPI — verify `policy/linux/SandboxBrokerPolicyFactory.cpp`). | +6 / 0 |
|
||||||
|
|
||||||
|
Total: roughly **+150 / -10** across 7 files.
|
||||||
|
|
||||||
|
## 2. Probe extension — `GfxInfo.cpp::V4L2ProbeDevice`
|
||||||
|
|
||||||
|
Existing pattern (~L1075):
|
||||||
|
```cpp
|
||||||
|
if (outFormats.Contains("H264")) { mIsV4L2Supported = Some(true);
|
||||||
|
mV4L2SupportedCodecs |= CODEC_HW_DEC_H264; ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
Add three siblings, each setting an additional `mV4L2IsStateless = true`:
|
||||||
|
```cpp
|
||||||
|
if (outFormats.Contains("S264")) { /* CODEC_HW_DEC_H264 | stateless */ }
|
||||||
|
if (outFormats.Contains("S265")) { /* CODEC_HW_DEC_HEVC | stateless */ }
|
||||||
|
if (outFormats.Contains("VP9F")) { /* CODEC_HW_DEC_VP9 | stateless */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
Decision: do **not** introduce a separate `mIsV4L2StatelessSupported`.
|
||||||
|
Collapse under `mIsV4L2Supported` so the existing feature-gate plumbing
|
||||||
|
(`MediaCodecsSupport`, `gfxFeature::HW_DECODE_VIDEO`) flips identically
|
||||||
|
— only `mV4L2IsStateless` distinguishes the routing in step 3. Stateful
|
||||||
|
+ stateless on the same SoC (rare, but RK3588 has both rkvdec2 + hantro
|
||||||
|
VEPU) gracefully degrades to whichever codec wins enumeration order.
|
||||||
|
|
||||||
|
The capture-format gate (`YV12`/`NV12`) needs widening: stateless
|
||||||
|
decoders frequently expose only `NV12` or `NV15` (10-bit, RK3588 HEVC).
|
||||||
|
Add `NV15` and `NM12` (multiplanar NV12, hantro). Without this the
|
||||||
|
prober rejects an otherwise-good device.
|
||||||
|
|
||||||
|
## 3. Decoder routing — `FFmpegVideoDecoder.cpp`
|
||||||
|
|
||||||
|
Codec selection happens at **L651** (`AV_HWDEVICE_TYPE_VAAPI` →
|
||||||
|
`h264_vaapi`) and **L708** (the V4L2 fallback path → `h264_v4l2m2m` via
|
||||||
|
`FindVideoHardwareAVCodec(mLib, mCodecID)` resolving by suffix). The
|
||||||
|
stateless route diverges from both: the *codec* must remain the generic
|
||||||
|
`h264`/`hevc`/`vp9` decoder (libavcodec auto-binds `v4l2_request` from
|
||||||
|
its `hw_configs` when a DRM hw_device_ctx is attached). Pseudo-patch:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if (gfxInfo.mUsingV4L2 && gfxInfo.mV4L2IsStateless) {
|
||||||
|
AVCodec* codec = mLib->avcodec_find_decoder(mCodecID); // generic
|
||||||
|
mCodecContext = mLib->avcodec_alloc_context3(codec);
|
||||||
|
if (!CreateV4L2RequestDeviceContext()) return false;
|
||||||
|
mCodecContext->get_format = ChooseV4L2PixelFormat; // already returns DRM_PRIME
|
||||||
|
mUsingV4L2Request = true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`CreateV4L2RequestDeviceContext()` body:
|
||||||
|
```cpp
|
||||||
|
const char* drm = "/dev/dri/renderD128";
|
||||||
|
if (mLib->av_hwdevice_ctx_create(&mDRMDeviceContext,
|
||||||
|
AV_HWDEVICE_TYPE_DRM, drm, nullptr, 0) < 0) return false;
|
||||||
|
mCodecContext->hw_device_ctx = mLib->av_buffer_ref(mDRMDeviceContext);
|
||||||
|
```
|
||||||
|
|
||||||
|
`av_hwdevice_ctx_create` — not currently wrapped — is the entry point.
|
||||||
|
The codec's internal hwaccel selector then walks
|
||||||
|
`avcodec_get_hw_config()` and picks the entry whose `device_type ==
|
||||||
|
AV_HWDEVICE_TYPE_DRM` and `pix_fmt == AV_PIX_FMT_DRM_PRIME`, which is
|
||||||
|
the v4l2_request hwaccel registered in `libavcodec/v4l2_request_*.c`.
|
||||||
|
No `av_hwdevice_find_type_by_name("v4l2_request")` needed — stays an
|
||||||
|
internal libavcodec name.
|
||||||
|
|
||||||
|
## 4. Dmabuf / DRM_PRIME reuse
|
||||||
|
|
||||||
|
`ChooseV4L2PixelFormat` at L~258–270 already returns
|
||||||
|
`AV_PIX_FMT_DRM_PRIME` and is the *only* format the v4l2_request
|
||||||
|
hwaccel produces. The downstream consumer (DMABufSurfaceYUV import in
|
||||||
|
`FFmpegVideoFramePool.cpp`) is already DRM_PRIME-aware for the
|
||||||
|
stateful path — same code reads `AVDRMFrameDescriptor` from
|
||||||
|
`frame->data[0]`. **No new output handling required for NV12/YV12.**
|
||||||
|
|
||||||
|
10-bit caveat: RK3588 HEVC outputs `DRM_FORMAT_NV15` / `NV20`
|
||||||
|
(Mali-tile). Existing `WaylandDMABufSurface::CreateYUVSurface`
|
||||||
|
modifier list does not include `DRM_FORMAT_MOD_ARM_AFBC` or NV15
|
||||||
|
fourcc. Either reject 10-bit at probe (capture format gate above) or
|
||||||
|
extend `gfx/layers/DMABUFSurfaceImage.cpp` — out of scope for v1; gate
|
||||||
|
to NV12 only.
|
||||||
|
|
||||||
|
SAND format pollution Turner mentioned in bug 1969297 c#3 is
|
||||||
|
**Pi5-specific**; rkvdec/hantro do not produce SAND. Safe to ignore for
|
||||||
|
the Rockchip target.
|
||||||
|
|
||||||
|
## 5. Configuration
|
||||||
|
|
||||||
|
New pref:
|
||||||
|
```yaml
|
||||||
|
- name: media.ffmpeg.v4l2-request.enabled
|
||||||
|
type: RelaxedAtomicBool
|
||||||
|
value: @IS_LINUX@
|
||||||
|
mirror: always
|
||||||
|
```
|
||||||
|
|
||||||
|
No new env var. No `MOZ_X11_EGL`-style kludge. The existing
|
||||||
|
`MOZ_LOG=PlatformDecoderModule:5` covers diagnostics. Default-on
|
||||||
|
matches `media.ffmpeg.vaapi.enabled` shape; users get fallback to
|
||||||
|
software via existing failure paths if `av_hwdevice_ctx_create` fails
|
||||||
|
(e.g., missing `/dev/media0`).
|
||||||
|
|
||||||
|
## 6. Test plan (fresnel — RK3399, KDE Wayland)
|
||||||
|
|
||||||
|
1. `ls /dev/video* /dev/media*` — confirm `/dev/video0` (rkvdec
|
||||||
|
output) and `/dev/media0` exist.
|
||||||
|
2. `v4l2-ctl -d /dev/video0 --list-formats-out` — expect
|
||||||
|
`S264`/`S265`/`VP9F`.
|
||||||
|
3. Start: `MOZ_LOG="PlatformDecoderModule:5,FFmpegVideo:5"
|
||||||
|
firefox-fourier 2>&1 | tee fx.log`.
|
||||||
|
4. Open `https://test-videos.co.uk/bigbuckbunny/mp4-h264` 1080p clip.
|
||||||
|
5. Success markers in `fx.log`:
|
||||||
|
- `V4L2ProbeDevice: /dev/video0 supports S264 (stateless)`
|
||||||
|
- `Choosing FFmpeg pixel format for V4L2 video decoding.`
|
||||||
|
- `Requesting pixel format DRM PRIME`
|
||||||
|
- `av_hwdevice_ctx_create(DRM, /dev/dri/renderD128) ok`
|
||||||
|
- **No** `Using preferred software codec h264`.
|
||||||
|
6. `cat /sys/kernel/debug/clk/clk_summary | grep vdec` — clock should
|
||||||
|
be active during playback.
|
||||||
|
7. `top` — CPU < 40% on a single A72 core for 1080p H.264 (stock =
|
||||||
|
100% on all 6 cores).
|
||||||
|
|
||||||
|
## 7. Build + ship — `firefox-fourier` PKGBUILD
|
||||||
|
|
||||||
|
Mirror `chromium-fourier` shape exactly (sibling).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pkgname=firefox-fourier
|
||||||
|
pkgver=149.0
|
||||||
|
arch=('aarch64' 'x86_64')
|
||||||
|
makedepends=(rust clang lld nodejs python cbindgen nasm yasm wasi-libc-bin
|
||||||
|
gtk3 mesa libva ffmpeg) # ffmpeg only for headers via system libs
|
||||||
|
source=(
|
||||||
|
"https://archive.mozilla.org/pub/firefox/releases/${pkgver}/source/firefox-${pkgver}.source.tar.xz"
|
||||||
|
patches/0001-gfxinfo-v4l2-stateless-fourccs.patch
|
||||||
|
patches/0002-libwrapper-hwdevice-ctx-create.patch
|
||||||
|
patches/0003-ffmpegvideo-v4l2-request-route.patch
|
||||||
|
patches/0004-prefs-v4l2-request.patch
|
||||||
|
mozconfig
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
`prepare()`: `cd firefox-${pkgver}` → apply patches with `patch -Np1`.
|
||||||
|
`build()`: `MOZ_NOSPAM=1 ./mach build`. `package()`: `./mach install
|
||||||
|
DESTDIR=${pkgdir}`. `mozconfig` enables
|
||||||
|
`--enable-default-toolkit=cairo-gtk3-wayland`, `--with-system-ffmpeg`,
|
||||||
|
`ac_add_options --disable-tests`. **No** `--enable-media-gpu-process`
|
||||||
|
— let it default. Tarball is the official Mozilla source release (not
|
||||||
|
gecko-dev).
|
||||||
|
|
||||||
|
Extra makedepends vs. stock firefox PKGBUILD: none — this only
|
||||||
|
modifies existing C++.
|
||||||
|
|
||||||
|
## 8. Risk register (ranked)
|
||||||
|
|
||||||
|
1. **libavcodec ABI mismatch.** ALARM ships ffmpeg 7.x; Firefox dlopens
|
||||||
|
whatever's at `libavcodec.so.61`. If the v4l2_request hwaccel was
|
||||||
|
compiled out (Arch's ffmpeg has it; ALARM rebuild may not),
|
||||||
|
`av_hwdevice_ctx_create(DRM, ...)` succeeds but no codec binds —
|
||||||
|
silent fallback. Mitigation: `ffmpeg -hwaccels` should list `drm`.
|
||||||
|
2. **Renderer-process sandbox** blocks `/dev/dri/renderD128` open.
|
||||||
|
VAAPI already brokered this for RDD process; verify
|
||||||
|
`SandboxBrokerPolicyFactory.cpp` covers `/dev/media*` too — likely
|
||||||
|
doesn't.
|
||||||
|
3. **glxtest probe runs in stripped env.** `v4l2test` (the
|
||||||
|
FireTestProcess child) needs `cap_sys_admin` for
|
||||||
|
`VIDIOC_S_EXT_CTRLS` request API ioctls? No — request API just
|
||||||
|
needs `O_RDWR` on `/dev/media*`. Should be fine.
|
||||||
|
4. **Regression of stateful path.** Adding new fourccs is additive;
|
||||||
|
the routing branch is gated on `mV4L2IsStateless`. Stateful boards
|
||||||
|
(Pi4) untouched.
|
||||||
|
5. **NV15/10-bit on RK3588** — explicitly out-of-scope v1;
|
||||||
|
gate-rejected.
|
||||||
|
6. **rkvdec2 driver maturity.** Linux 6.12 mainline rkvdec2 H.264
|
||||||
|
works; HEVC/VP9 still upstream-pending on some boards. Probe will
|
||||||
|
skip what kernel doesn't expose.
|
||||||
|
7. **DMA-BUF modifier negotiation** with panfrost/panthor on Wayland
|
||||||
|
— already shaken out by chromium-fourier on RK3566; same code
|
||||||
|
path.
|
||||||
|
|
||||||
|
## 9. Upstream path (bug 1969297)
|
||||||
|
|
||||||
|
Split into 4 reviewable commits matching files 1, 2+3, 5, 6 from the
|
||||||
|
table. Add a gtest exercising `V4L2ProbeDevice` against a synthetic
|
||||||
|
v4l2test stdout containing `S264` (no kernel needed). Reach out to
|
||||||
|
skyevg (D252119 author) for review continuity. r? jya for the
|
||||||
|
FFmpegVideoDecoder change. The `av_hwdevice_ctx_create` wrapper
|
||||||
|
addition is a self-contained 6-liner that should land independently.
|
||||||
|
The 10-bit/SAND concerns Turner raised remain valid for Pi5 —
|
||||||
|
explicitly scope this series to **stateless DRM_PRIME NV12 only**,
|
||||||
|
leaving SAND for a follow-up bug.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# firefox-fourier mozconfig — minimal, Wayland + system ffmpeg.
|
||||||
|
|
||||||
|
ac_add_options --enable-application=browser
|
||||||
|
ac_add_options --enable-default-toolkit=cairo-gtk3-wayland
|
||||||
|
ac_add_options --enable-release
|
||||||
|
ac_add_options --enable-optimize
|
||||||
|
ac_add_options --enable-rust-simd
|
||||||
|
ac_add_options --enable-linker=lld
|
||||||
|
|
||||||
|
# Arch's 0004 patch updates the wasm32-wasip1 target string but ALARM's
|
||||||
|
# wasi-libc package doesn't expose the headers at the path Mozilla's
|
||||||
|
# probe looks for. Disable the wasm sandbox — hardens font/graphics
|
||||||
|
# parsers only, no impact on V4L2 decode. Revisit when ALARM's
|
||||||
|
# wasi-libc catches up to Arch x86_64's layout.
|
||||||
|
ac_add_options --without-wasm-sandboxed-libraries
|
||||||
|
|
||||||
|
# Firefox dlopens libavcodec.so at runtime regardless of build flags;
|
||||||
|
# the v4l2_request hwaccel routing happens via the system libavcodec
|
||||||
|
# loaded at startup, controlled by media.ffmpeg.enabled (default true).
|
||||||
|
# No configure-time hook needed.
|
||||||
|
|
||||||
|
ac_add_options --disable-tests
|
||||||
|
ac_add_options --disable-debug
|
||||||
|
ac_add_options --disable-debug-symbols
|
||||||
|
ac_add_options --disable-crashreporter
|
||||||
|
ac_add_options --disable-updater
|
||||||
|
ac_add_options --disable-default-browser-agent
|
||||||
|
|
||||||
|
# Mozilla branding requires a separate signed-build-tooling agreement
|
||||||
|
# we don't have; ship with the unbranded "firefox-fourier" identity.
|
||||||
|
ac_add_options --with-app-name=firefox-fourier
|
||||||
|
ac_add_options --with-app-basename=Firefox
|
||||||
|
ac_add_options --with-distribution-id=de.reauktion.fourier
|
||||||
|
|
||||||
|
# Reduce build memory pressure on aarch64 — parallel link is heavy.
|
||||||
|
mk_add_options MOZ_PARALLEL_BUILD=8
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
Subject: [PATCH 1/4] widget/gtk: recognize V4L2 stateless fourccs in
|
||||||
|
GfxInfo prober (S264 / S265 / VP9F)
|
||||||
|
Date: 2026-04-27
|
||||||
|
|
||||||
|
Background
|
||||||
|
----------
|
||||||
|
Firefox's V4L2 prober in `widget/gtk/GfxInfo.cpp::V4L2ProbeDevice`
|
||||||
|
parses `v4l2test`'s `V4L2_OUTPUT_FMTS` line and matches against the
|
||||||
|
fourccs of stateful V4L2-M2M decoders (`H264`, `VP80`, `VP90`, `HEVC`).
|
||||||
|
That's correct for Pi4 / Mediatek / vendor-MPP stateful decoders but
|
||||||
|
silently skips every mainline-Linux Rockchip board: RK3399 `rkvdec`,
|
||||||
|
RK3566 `hantro` (multiplanar), RK3588 `hantro` and `rkvdec2` all
|
||||||
|
expose stateless fourccs only — `S264`, `S265`, `VP9F`. The probe
|
||||||
|
binary itself enumerates these correctly (verified end-to-end on
|
||||||
|
fresnel / Pinebook Pro / RK3399 with `v4l2test --device /dev/video1`
|
||||||
|
showing `V4L2_OUTPUT_FMTS: S265 S264 VP9F` and
|
||||||
|
`V4L2_SUPPORTED: TRUE`); the gap is purely in this string table.
|
||||||
|
|
||||||
|
This patch adds the three sibling blocks for the stateless fourccs,
|
||||||
|
each identical in shape to the existing stateful blocks except for
|
||||||
|
setting a new `mV4L2IsStateless` member. The follow-up patches in
|
||||||
|
this series (2/4, 3/4, 4/4) consume that member to route through the
|
||||||
|
libavcodec v4l2_request hwaccel (`AV_HWDEVICE_TYPE_DRM`) instead of
|
||||||
|
the v4l2m2m codec wrapper used for stateful boards.
|
||||||
|
|
||||||
|
Bug 1969297.
|
||||||
|
|
||||||
|
diff --git a/widget/gtk/GfxInfo.h b/widget/gtk/GfxInfo.h
|
||||||
|
--- a/widget/gtk/GfxInfo.h
|
||||||
|
+++ b/widget/gtk/GfxInfo.h
|
||||||
|
@@ -127,6 +127,10 @@
|
||||||
|
mozilla::Maybe<bool> mIsVAAPISupported;
|
||||||
|
int mVAAPISupportedCodecs = 0;
|
||||||
|
mozilla::Maybe<bool> mIsV4L2Supported;
|
||||||
|
+ // firefox-fourier: true when probe matched at least one stateless
|
||||||
|
+ // V4L2 fourcc (S264 / S265 / VP9F). Drives libavcodec v4l2_request
|
||||||
|
+ // hwaccel routing in FFmpegVideoDecoder.cpp.
|
||||||
|
+ bool mV4L2IsStateless = false;
|
||||||
|
int mV4L2SupportedCodecs = 0;
|
||||||
|
|
||||||
|
static int sGLXTestPipe;
|
||||||
|
diff --git a/widget/gtk/GfxInfo.cpp b/widget/gtk/GfxInfo.cpp
|
||||||
|
--- a/widget/gtk/GfxInfo.cpp
|
||||||
|
+++ b/widget/gtk/GfxInfo.cpp
|
||||||
|
@@ -852,6 +852,29 @@ void GfxInfo::V4L2ProbeDevice(nsCString& dev) {
|
||||||
|
media::MCSInfo::AddSupport(media::MediaCodecsSupport::HEVCHardwareDecode);
|
||||||
|
mV4L2SupportedCodecs |= CODEC_HW_DEC_HEVC;
|
||||||
|
}
|
||||||
|
+ // firefox-fourier: V4L2 stateless (request API) fourccs. Mainline
|
||||||
|
+ // Rockchip rkvdec / hantro / rkvdec2 expose these instead of the
|
||||||
|
+ // V4L2-M2M-stateful fourccs above. Decoding routes through
|
||||||
|
+ // libavcodec's v4l2_request hwaccel (AV_HWDEVICE_TYPE_DRM) rather
|
||||||
|
+ // than the *_v4l2m2m codec wrappers — see FFmpegVideoDecoder.cpp.
|
||||||
|
+ if (outFormats.Contains("S264")) {
|
||||||
|
+ mIsV4L2Supported = Some(true);
|
||||||
|
+ mV4L2IsStateless = true;
|
||||||
|
+ media::MCSInfo::AddSupport(media::MediaCodecsSupport::H264HardwareDecode);
|
||||||
|
+ mV4L2SupportedCodecs |= CODEC_HW_DEC_H264;
|
||||||
|
+ }
|
||||||
|
+ if (outFormats.Contains("S265")) {
|
||||||
|
+ mIsV4L2Supported = Some(true);
|
||||||
|
+ mV4L2IsStateless = true;
|
||||||
|
+ media::MCSInfo::AddSupport(media::MediaCodecsSupport::HEVCHardwareDecode);
|
||||||
|
+ mV4L2SupportedCodecs |= CODEC_HW_DEC_HEVC;
|
||||||
|
+ }
|
||||||
|
+ if (outFormats.Contains("VP9F")) {
|
||||||
|
+ mIsV4L2Supported = Some(true);
|
||||||
|
+ mV4L2IsStateless = true;
|
||||||
|
+ media::MCSInfo::AddSupport(media::MediaCodecsSupport::VP9HardwareDecode);
|
||||||
|
+ mV4L2SupportedCodecs |= CODEC_HW_DEC_VP9;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
const nsTArray<RefPtr<GfxDriverInfo>>& GfxInfo::GetGfxDriverInfo() {
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
Subject: [PATCH 2/4] dom/media/platforms/ffmpeg: wrap
|
||||||
|
av_hwdevice_ctx_create
|
||||||
|
Date: 2026-04-27
|
||||||
|
|
||||||
|
Background
|
||||||
|
----------
|
||||||
|
`FFmpegLibWrapper` already wraps `av_hwdevice_ctx_alloc` (no device
|
||||||
|
path) and `av_hwdevice_ctx_init`, used by the VAAPI codepath which
|
||||||
|
discovers the DRM device implicitly. The v4l2_request hwaccel needs
|
||||||
|
the *path-aware* constructor `av_hwdevice_ctx_create`, which lets the
|
||||||
|
caller pass `"/dev/dri/renderD128"` (or similar) directly when
|
||||||
|
creating an `AV_HWDEVICE_TYPE_DRM` context. libavcodec then binds the
|
||||||
|
v4l2_request hwaccel internally based on the codec's `hw_configs`.
|
||||||
|
|
||||||
|
This patch adds the function pointer + the `AV_FUNC_OPTION_SILENT`
|
||||||
|
registration. Same versioning as the other `av_hwdevice_ctx_*`
|
||||||
|
wrappers (libavutil 58–62). No callers yet — patch 3/4
|
||||||
|
(FFmpegVideoDecoder routing) consumes it.
|
||||||
|
|
||||||
|
Bug 1969297.
|
||||||
|
|
||||||
|
diff --git a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
|
||||||
|
--- a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
|
||||||
|
+++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
|
||||||
|
@@ -177,6 +177,11 @@
|
||||||
|
// libavutil >= 58
|
||||||
|
AVBufferRef* (*av_hwdevice_ctx_alloc)(int);
|
||||||
|
int (*av_hwdevice_ctx_init)(AVBufferRef* ref);
|
||||||
|
+ // firefox-fourier: device-path-aware constructor needed to bind a
|
||||||
|
+ // DRM hwdevice (AV_HWDEVICE_TYPE_DRM) to /dev/dri/renderD* for the
|
||||||
|
+ // libavcodec v4l2_request hwaccel.
|
||||||
|
+ int (*av_hwdevice_ctx_create)(AVBufferRef** device_ctx, int type,
|
||||||
|
+ const char* device, void* opts, int flags);
|
||||||
|
AVBufferRef* (*av_hwframe_ctx_alloc)(AVBufferRef* device_ctx);
|
||||||
|
int (*av_hwframe_ctx_init)(AVBufferRef* ref);
|
||||||
|
AVBufferRef* (*av_buffer_ref)(AVBufferRef* buf);
|
||||||
|
diff --git a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
|
||||||
|
--- a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
|
||||||
|
+++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
|
||||||
|
@@ -293,6 +293,11 @@ FFmpegLibWrapper::LinkResult FFmpegLibWrapper::Link() {
|
||||||
|
AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59 |
|
||||||
|
AV_FUNC_AVUTIL_60 | AV_FUNC_AVUTIL_61 |
|
||||||
|
AV_FUNC_AVUTIL_62)
|
||||||
|
+ // firefox-fourier: see comment in FFmpegLibWrapper.h
|
||||||
|
+ AV_FUNC_OPTION_SILENT(av_hwdevice_ctx_create,
|
||||||
|
+ AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59 |
|
||||||
|
+ AV_FUNC_AVUTIL_60 | AV_FUNC_AVUTIL_61 |
|
||||||
|
+ AV_FUNC_AVUTIL_62)
|
||||||
|
AV_FUNC_OPTION_SILENT(
|
||||||
|
av_buffer_ref, AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59 | AV_FUNC_AVUTIL_60 |
|
||||||
|
AV_FUNC_AVUTIL_61 | AV_FUNC_AVUTIL_62)
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
Subject: [PATCH 3/4] dom/media/platforms/ffmpeg: route through libavcodec
|
||||||
|
v4l2_request hwaccel for V4L2 stateless boards
|
||||||
|
Date: 2026-04-27
|
||||||
|
|
||||||
|
Background
|
||||||
|
----------
|
||||||
|
Firefox's existing V4L2 init (`InitV4L2Decoder`) finds the codec by
|
||||||
|
suffix lookup (`FindVideoHardwareAVCodec(mLib, mCodecID)`), which on
|
||||||
|
Linux resolves to the **stateful** V4L2-M2M wrapper codec
|
||||||
|
(`h264_v4l2m2m` etc). Mainline-Linux Rockchip boards (RK3399 rkvdec,
|
||||||
|
RK3566/RK3588 hantro, RK3588 rkvdec2) only expose **stateless**
|
||||||
|
V4L2 fourccs (`S264`, `S265`, `VP9F`); the stateful wrapper codec
|
||||||
|
fails to open, Firefox falls all the way through to software.
|
||||||
|
|
||||||
|
This patch adds a sibling init path, `InitV4L2RequestDecoder`, that:
|
||||||
|
|
||||||
|
* uses the **generic** codec (e.g. plain `h264`, returned by
|
||||||
|
`avcodec_find_decoder(AV_CODEC_ID_H264)`) rather than the stateful
|
||||||
|
wrapper;
|
||||||
|
* sanity-checks the codec's `hw_configs` for an `AV_HWDEVICE_TYPE_DRM`
|
||||||
|
entry — that's how libavcodec surfaces the v4l2_request hwaccel
|
||||||
|
upstream (no dedicated `_V4L2REQUEST` device type exists);
|
||||||
|
* creates an `AV_HWDEVICE_TYPE_DRM` hwdevice context bound to
|
||||||
|
`/dev/dri/renderD128` via the new `av_hwdevice_ctx_create` wrapper
|
||||||
|
(patch 2/4) and attaches it to the codec context;
|
||||||
|
* reuses the existing `ChooseV4L2PixelFormat` get-format callback
|
||||||
|
(already returns `AV_PIX_FMT_DRM_PRIME`) and the existing
|
||||||
|
`apply_cropping = 0` constraint.
|
||||||
|
|
||||||
|
`InitV4L2RequestDecoder` is invoked **before** `InitV4L2Decoder` in
|
||||||
|
`InitHWDecoderIfAllowed`. On Rockchip mainline it succeeds. On Pi4 /
|
||||||
|
Mediatek / vendor-MPP-stateful boards the codec's `hw_configs` lacks
|
||||||
|
a DRM entry (the V4L2-M2M codecs don't register one), the sanity
|
||||||
|
check fails, and the existing stateful `InitV4L2Decoder` runs as
|
||||||
|
before. No regression of stateful boards.
|
||||||
|
|
||||||
|
`mDRMDeviceContext` is unconditionally `av_buffer_unref`'d in
|
||||||
|
`ProcessShutdown` (no-op when null). Gated behind
|
||||||
|
`media.ffmpeg.v4l2-request.enabled` from patch 4/4.
|
||||||
|
|
||||||
|
Bug 1969297.
|
||||||
|
|
||||||
|
diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
|
||||||
|
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h 2026-03-18 19:22:14.000000000 +0000
|
||||||
|
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h 2026-04-27 20:43:39.347992674 +0000
|
||||||
|
@@ -225,7 +225,12 @@
|
||||||
|
bool IsLinuxHDR() const;
|
||||||
|
MediaResult InitVAAPIDecoder();
|
||||||
|
MediaResult InitV4L2Decoder();
|
||||||
|
+ // firefox-fourier: V4L2 stateless (request API) decode path. Uses
|
||||||
|
+ // libavcodec's v4l2_request hwaccel, which it surfaces via
|
||||||
|
+ // AV_HWDEVICE_TYPE_DRM rather than a dedicated _V4L2REQUEST type.
|
||||||
|
+ MediaResult InitV4L2RequestDecoder();
|
||||||
|
bool CreateVAAPIDeviceContext();
|
||||||
|
+ bool CreateV4L2RequestDeviceContext();
|
||||||
|
bool GetVAAPISurfaceDescriptor(VADRMPRIMESurfaceDescriptor* aVaDesc);
|
||||||
|
void AddAcceleratedFormats(nsTArray<AVCodecID>& aCodecList,
|
||||||
|
AVCodecID aCodecID, AVVAAPIHWConfig* hwconfig);
|
||||||
|
@@ -239,7 +244,10 @@
|
||||||
|
void AdjustHWDecodeLogging();
|
||||||
|
|
||||||
|
AVBufferRef* mVAAPIDeviceContext = nullptr;
|
||||||
|
+ // firefox-fourier: DRM hwdevice ctx for the v4l2_request hwaccel.
|
||||||
|
+ AVBufferRef* mDRMDeviceContext = nullptr;
|
||||||
|
bool mUsingV4L2 = false;
|
||||||
|
+ bool mUsingV4L2Request = false;
|
||||||
|
// If video overlay is used we want to upload SW decoded frames to
|
||||||
|
// DMABuf and present it as a external texture to rendering pipeline.
|
||||||
|
bool mUploadSWDecodeToDMABuf = false;
|
||||||
|
diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
|
||||||
|
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp 2026-03-18 19:22:14.000000000 +0000
|
||||||
|
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp 2026-04-27 20:44:33.280766228 +0000
|
||||||
|
@@ -406,6 +406,105 @@
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
+// firefox-fourier: V4L2 stateless (request API) DRM hwdevice context.
|
||||||
|
+// libavcodec's v4l2_request hwaccel binds via AV_HWDEVICE_TYPE_DRM —
|
||||||
|
+// no dedicated _V4L2REQUEST type exists upstream.
|
||||||
|
+bool FFmpegVideoDecoder<LIBAV_VER>::CreateV4L2RequestDeviceContext() {
|
||||||
|
+ if (!mLib->av_hwdevice_ctx_create) {
|
||||||
|
+ FFMPEG_LOG(" av_hwdevice_ctx_create not available (libavutil too old)");
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ const char* drmDevice = "/dev/dri/renderD128";
|
||||||
|
+ if (mLib->av_hwdevice_ctx_create(&mDRMDeviceContext,
|
||||||
|
+ AV_HWDEVICE_TYPE_DRM, drmDevice,
|
||||||
|
+ nullptr, 0) < 0) {
|
||||||
|
+ FFMPEG_LOG(" av_hwdevice_ctx_create(DRM, %s) failed", drmDevice);
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ mCodecContext->hw_device_ctx = mLib->av_buffer_ref(mDRMDeviceContext);
|
||||||
|
+ FFMPEG_LOG(" DRM hwdevice ctx created on %s", drmDevice);
|
||||||
|
+ return true;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// firefox-fourier: try V4L2 stateless decode via libavcodec's
|
||||||
|
+// v4l2_request hwaccel. Distinct from InitV4L2Decoder which uses the
|
||||||
|
+// stateful h264_v4l2m2m wrapper codec. On Rockchip mainline boards
|
||||||
|
+// (rkvdec / hantro / rkvdec2) only the stateless path exists.
|
||||||
|
+MediaResult FFmpegVideoDecoder<LIBAV_VER>::InitV4L2RequestDecoder() {
|
||||||
|
+ FFMPEG_LOG("Initialising V4L2 stateless (request API) FFmpeg decoder");
|
||||||
|
+
|
||||||
|
+ StaticMutexAutoLock mon(sMutex);
|
||||||
|
+
|
||||||
|
+ // Use the GENERIC codec (not the _v4l2m2m wrapper). libavcodec picks
|
||||||
|
+ // the v4l2_request hwaccel internally by walking the codec's
|
||||||
|
+ // hw_configs once an AV_HWDEVICE_TYPE_DRM ctx is attached.
|
||||||
|
+ AVCodec* codec = mLib->avcodec_find_decoder(mCodecID);
|
||||||
|
+ if (!codec) {
|
||||||
|
+ FFMPEG_LOG(" generic codec for ID %d not found", mCodecID);
|
||||||
|
+ return NS_ERROR_NOT_AVAILABLE;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // Sanity-check: refuse when libavcodec was built without the
|
||||||
|
+ // v4l2_request hwaccel (no DRM hwaccel registered against this
|
||||||
|
+ // codec). Distro packagers occasionally drop --enable-v4l2-request.
|
||||||
|
+ bool hasDrmHwaccel = false;
|
||||||
|
+ for (int i = 0;; i++) {
|
||||||
|
+ const AVCodecHWConfig* cfg = mLib->avcodec_get_hw_config(codec, i);
|
||||||
|
+ if (!cfg) break;
|
||||||
|
+ if (cfg->device_type == AV_HWDEVICE_TYPE_DRM) {
|
||||||
|
+ hasDrmHwaccel = true;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (!hasDrmHwaccel) {
|
||||||
|
+ FFMPEG_LOG(" codec %s has no DRM hwaccel — libavcodec built "
|
||||||
|
+ "without --enable-v4l2-request?", codec->name);
|
||||||
|
+ return NS_ERROR_NOT_AVAILABLE;
|
||||||
|
+ }
|
||||||
|
+ FFMPEG_LOG(" V4L2 stateless: codec %s : %s", codec->name, codec->long_name);
|
||||||
|
+
|
||||||
|
+ if (!(mCodecContext = mLib->avcodec_alloc_context3(codec))) {
|
||||||
|
+ FFMPEG_LOG(" couldn't init HW ffmpeg context");
|
||||||
|
+ return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
+ }
|
||||||
|
+ mCodecContext->opaque = this;
|
||||||
|
+
|
||||||
|
+ // Reuse the existing V4L2 init helpers: pixel-format selector returns
|
||||||
|
+ // AV_PIX_FMT_DRM_PRIME, cropping disabled (FFmpeg can't crop opaque
|
||||||
|
+ // DRM buffers). Same constraints as the stateful V4L2 path.
|
||||||
|
+ InitHWCodecContext(ContextType::V4L2);
|
||||||
|
+ mCodecContext->apply_cropping = 0;
|
||||||
|
+
|
||||||
|
+ auto releaseDecoder = MakeScopeExit(
|
||||||
|
+ [&]() MOZ_NO_THREAD_SAFETY_ANALYSIS { ReleaseCodecContext(); });
|
||||||
|
+
|
||||||
|
+ if (!CreateV4L2RequestDeviceContext()) {
|
||||||
|
+ return NS_ERROR_NOT_AVAILABLE;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ MediaResult ret = AllocateExtraData();
|
||||||
|
+ if (NS_FAILED(ret)) {
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (mLib->avcodec_open2(mCodecContext, codec, nullptr) < 0) {
|
||||||
|
+ FFMPEG_LOG(" Couldn't open V4L2 stateless decoder");
|
||||||
|
+ return NS_ERROR_DOM_MEDIA_FATAL_ERR;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (mAcceleratedFormats.IsEmpty()) {
|
||||||
|
+ mAcceleratedFormats.AppendElement(mCodecID);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ AdjustHWDecodeLogging();
|
||||||
|
+
|
||||||
|
+ FFMPEG_LOG(" V4L2 stateless FFmpeg init successful");
|
||||||
|
+ mUsingV4L2 = true;
|
||||||
|
+ mUsingV4L2Request = true;
|
||||||
|
+ releaseDecoder.release();
|
||||||
|
+ return NS_OK;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
MediaResult FFmpegVideoDecoder<LIBAV_VER>::InitV4L2Decoder() {
|
||||||
|
FFMPEG_LOG("Initialising V4L2-DRM FFmpeg decoder");
|
||||||
|
|
||||||
|
@@ -659,6 +758,16 @@
|
||||||
|
# endif // MOZ_ENABLE_VAAPI
|
||||||
|
|
||||||
|
# ifdef MOZ_ENABLE_V4L2
|
||||||
|
+ // firefox-fourier: try V4L2 stateless (request API) first. On
|
||||||
|
+ // mainline-Linux Rockchip boards (RK3399 rkvdec, RK3566/RK3588
|
||||||
|
+ // hantro, RK3588 rkvdec2) the kernel exposes only stateless
|
||||||
|
+ // fourccs, so the stateful path below would fail anyway. On
|
||||||
|
+ // stateful boards (Pi4 / vendor MPP) this gracefully falls
|
||||||
|
+ // through (no DRM hwaccel registered for the codec).
|
||||||
|
+ if (StaticPrefs::media_ffmpeg_v4l2_request_enabled() &&
|
||||||
|
+ NS_SUCCEEDED(InitV4L2RequestDecoder())) {
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
// VAAPI didn't work or is disabled, so try V4L2 with DRM
|
||||||
|
if (NS_SUCCEEDED(InitV4L2Decoder())) {
|
||||||
|
return;
|
||||||
|
@@ -2046,6 +2155,11 @@
|
||||||
|
if (IsHardwareAccelerated()) {
|
||||||
|
mLib->av_buffer_unref(&mVAAPIDeviceContext);
|
||||||
|
}
|
||||||
|
+ // firefox-fourier: release the DRM hwdevice ctx for the v4l2_request
|
||||||
|
+ // hwaccel. Always safe — av_buffer_unref no-ops on a null pointer.
|
||||||
|
+ if (mDRMDeviceContext) {
|
||||||
|
+ mLib->av_buffer_unref(&mDRMDeviceContext);
|
||||||
|
+ }
|
||||||
|
#endif
|
||||||
|
#ifdef MOZ_ENABLE_D3D11VA
|
||||||
|
if (IsHardwareAccelerated()) {
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
Subject: [PATCH 4/4] modules/libpref: add media.ffmpeg.v4l2-request.enabled
|
||||||
|
Date: 2026-04-27
|
||||||
|
|
||||||
|
Background
|
||||||
|
----------
|
||||||
|
Toggle for the V4L2 stateless (request API) decode path introduced
|
||||||
|
in patch 3/4. Defaults on for Linux, mirroring the
|
||||||
|
`media.ffmpeg.vaapi.enabled`-style shape. Users can flip to false to
|
||||||
|
force the existing stateful `InitV4L2Decoder` (or VAAPI / software
|
||||||
|
fallbacks) without rebuilding.
|
||||||
|
|
||||||
|
Bug 1969297.
|
||||||
|
|
||||||
|
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
|
||||||
|
--- a/modules/libpref/init/StaticPrefList.yaml
|
||||||
|
+++ b/modules/libpref/init/StaticPrefList.yaml
|
||||||
|
@@ -12159,6 +12159,16 @@
|
||||||
|
type: uint32_t
|
||||||
|
value: 2
|
||||||
|
mirror: once
|
||||||
|
+
|
||||||
|
+# firefox-fourier: route V4L2 stateless (request API) decode through
|
||||||
|
+# libavcodec's v4l2_request hwaccel (AV_HWDEVICE_TYPE_DRM). Required
|
||||||
|
+# for mainline-Linux Rockchip rkvdec / hantro / rkvdec2. On stateful
|
||||||
|
+# boards (Pi4 / vendor MPP) the codec's hw_configs lacks a DRM entry
|
||||||
|
+# and the path silently falls back to InitV4L2Decoder.
|
||||||
|
+- name: media.ffmpeg.v4l2-request.enabled
|
||||||
|
+ type: RelaxedAtomicBool
|
||||||
|
+ value: true
|
||||||
|
+ mirror: always
|
||||||
|
#endif # MOZ_WIDGET_GTK
|
||||||
|
|
||||||
|
# Set to true in marionette tests to disable the sanity test
|
||||||
Reference in New Issue
Block a user