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

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

firefox-fourier:
- 4 patches (gfxinfo v4l2 stateless fourccs, libwrapper hwdevice ctx,
  ffmpegvideo v4l2-request route, prefs v4l2-request default).
- PKGBUILD bumped to firefox 150.0.1, Arch toolchain glue patches
  layered in, mozconfig with --without-wasm-sandboxed-libraries for
  ALARM, package() launcher fix (rm -f symlink before cat > to avoid
  ENOENT through the dangling /usr/local symlink mach install drops).
- 150.0.1-1-aarch64.pkg.tar.zst built on boltzmann (95 MB), pending
  fresnel power-on for V4L2 stateless validation on RK3399.
This commit is contained in:
2026-04-28 12:02:18 +00:00
parent 7bb2fbeca9
commit 8756ce38be
15 changed files with 1711 additions and 60 deletions
@@ -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);