From 8245221755ee1fec2aa6a360c5c05980f2c44aa3 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sat, 3 Oct 2026 15:54:55 +0800 Subject: [PATCH 1/6] [Fix] (Remote): name a shared image by extent and format when the kernel gives descriptors one identity (st_dev, st_ino) is how an incoming dma-buf descriptor names the image it was exported from, and it is the only identity the registry had. Some kernels do not keep those apart: on a 4.14 vendor kernel every descriptor of every image reports dev 12 ino 10658 with st_size 0, and /proc/self/fdinfo carries nothing else, so the registry correctly detected the ambiguity and then refused every import - which is every zero-copy window a compositor ever tries to sample, leaving the whole stack on the wl_shm fallback. The identity stays the kernel's to give. When it is ambiguous, the import's own extent and format select among the live images exported under that identity, in allocation order and each claimed once, which is the order a producer allocates and hands them over in. Identify() takes the extent and the fourcc the import record already carries; nothing else changes. --- MobileGL/MG_Remote/Server/PipeApplier.cpp | 2 +- .../MG_Remote/Server/SharedImageRegistry.cpp | 34 ++++++++++++++++--- .../MG_Remote/Server/SharedImageRegistry.h | 16 +++++++-- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/MobileGL/MG_Remote/Server/PipeApplier.cpp b/MobileGL/MG_Remote/Server/PipeApplier.cpp index 659d1ceb..29d083ea 100644 --- a/MobileGL/MG_Remote/Server/PipeApplier.cpp +++ b/MobileGL/MG_Remote/Server/PipeApplier.cpp @@ -1539,7 +1539,7 @@ namespace MobileGL::MG_Remote::Server { static_cast(seq), why.c_str()); return false; } - SI::ImageRef image = SI::Identify(fd, why); + SI::ImageRef image = SI::Identify(fd, op.Width, op.Height, op.Format, why); #if !defined(_WIN32) ::close(fd); #endif diff --git a/MobileGL/MG_Remote/Server/SharedImageRegistry.cpp b/MobileGL/MG_Remote/Server/SharedImageRegistry.cpp index d6010906..71d10739 100644 --- a/MobileGL/MG_Remote/Server/SharedImageRegistry.cpp +++ b/MobileGL/MG_Remote/Server/SharedImageRegistry.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #if defined(__ANDROID__) @@ -43,6 +44,9 @@ namespace MobileGL::MG_Remote::Server::SharedImages { std::atomic nextId{1}; UnorderedMap> byId; std::map, std::weak_ptr> byIdentity; + // Images an ambiguous-identity import has already named, so the next one takes the + // next image the server exported under that identity instead of naming this one twice. + std::set claimedByIdentity; // Set once two live images were seen with one identity: the kernel shares an inode // between dma-bufs, so a descriptor can no longer say which image it is. Bool identityAmbiguous = false; @@ -226,7 +230,8 @@ namespace MobileGL::MG_Remote::Server::SharedImages { if (!reg.identityAmbiguous) { MGLOG_E("MG_Remote server: two live shared images have one dma-buf identity (dev %llu ino %llu); " "this kernel does not keep dma-buf inodes unique, so a returned descriptor cannot name its " - "image and every shared-image import is refused from here on", + "image. Imports degrade to naming it by extent and format over the images exported under " + "that identity, in allocation order (SharedImages::Identify)", static_cast(key.first), static_cast(key.second)); } reg.identityAmbiguous = true; @@ -235,9 +240,9 @@ namespace MobileGL::MG_Remote::Server::SharedImages { return image; } - ImageRef Identify(int fd, std::string& why) { + ImageRef Identify(int fd, Uint32 width, Uint32 height, Uint32 fourcc, std::string& why) { #if defined(_WIN32) - (void)fd; + (void)fd, (void)width, (void)height, (void)fourcc; why = "shared images need POSIX descriptors"; return nullptr; #else @@ -248,9 +253,28 @@ namespace MobileGL::MG_Remote::Server::SharedImages { } Registry& reg = Reg(); const std::lock_guard lock(reg.mutex); + // A KERNEL THAT DOES NOT NAME ITS BUFFERS. Every descriptor of every image reports one + // identity here, so the descriptor cannot say which image it is; the import's own extent + // and format do, over the images exported under that identity, oldest first and each + // claimed once - the order the producer allocated and hands them over in. if (reg.identityAmbiguous) { - why = "dma-buf identities are not unique on this kernel"; - return nullptr; + ImageRef chosen; + for (const auto& [id, weak] : reg.byId) { + const ImageRef image = weak.lock(); + if (image == nullptr || image->IdentityDev != dev || image->IdentityIno != ino) continue; + if (image->Width != width || image->Height != height || image->Fourcc != fourcc) continue; + if (reg.claimedByIdentity.count(image->Id) != 0) continue; + if (chosen == nullptr || image->Id < chosen->Id) chosen = image; + } + if (chosen == nullptr) { + why = Format("no unclaimed live image of this extent and format was exported under the " + "only identity this kernel reports for descriptors (%llux%llu fourcc %llx)", + static_cast(width) | (static_cast(height) << 32), + static_cast(fourcc), 0); + return nullptr; + } + reg.claimedByIdentity.insert(chosen->Id); + return chosen; } const auto it = reg.byIdentity.find(std::make_pair(dev, ino)); ImageRef image = it != reg.byIdentity.end() ? it->second.lock() : nullptr; diff --git a/MobileGL/MG_Remote/Server/SharedImageRegistry.h b/MobileGL/MG_Remote/Server/SharedImageRegistry.h index dfd28c04..21b5e59a 100644 --- a/MobileGL/MG_Remote/Server/SharedImageRegistry.h +++ b/MobileGL/MG_Remote/Server/SharedImageRegistry.h @@ -87,9 +87,19 @@ namespace MobileGL::MG_Remote::Server::SharedImages { // A new image of `width` x `height` `fourcc`, registered. Null with `why` on failure. ImageRef Allocate(Uint32 width, Uint32 height, Uint32 fourcc, std::string& why); - // The live image `fd` was exported from, or null (`why` says which test failed): a descriptor - // this server never exported, or one whose identity the kernel does not keep unique. - ImageRef Identify(int fd, std::string& why); + // The live image the descriptor was exported from, or null (why says which test failed): a + // descriptor this server never exported, or one no live image matches. + // + // WHICH IDENTITY A DESCRIPTOR CARRIES IS THE KERNEL'S TO SAY. (st_dev, st_ino) is the + // honest answer, and the only one on kernels that give every dma-buf its own inode. Some + // do not: there, every descriptor reports one identity (measured on a 4.14 vendor kernel: + // dev 12, ino 10658, st_size 0, and /proc/self/fdinfo carries nothing else either), so the + // descriptor cannot name its image. The import then names it by what it DOES carry - the + // extent and the format it declares - and the images the server exported under that + // identity are taken in allocation order, each claimable once, which is the order the + // producer hands them over. A kernel whose descriptors are ambiguous degrades to that, + // and says so once. + ImageRef Identify(int fd, Uint32 width, Uint32 height, Uint32 fourcc, std::string& why); // The live image with this id, or null. ImageRef Find(Uint64 id); From f44888ca01f7ea1c5a9f13a5c048700e9aa9da76 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sat, 3 Oct 2026 15:54:55 +0800 Subject: [PATCH 2/6] [Fix] (Espryt): present through eglSwapBuffers when the driver refuses the damage entry point A vendor EGL can export eglSwapBuffersWithDamageEXT and then answer EGL_FALSE on a window surface without setting an error (measured on an Android 4.14 vendor kernel with a Mali driver; the surface is current, the context is current, the ANativeWindow reports its full extent, and the plain entry point presents the same surface happily). Trusting that pointer kept the frame: the window's buffer queue never saw a buffer, the compositor's own window stayed a solid colour on screen, and every client of the session fell back to wl_shm because the compositor could not sample what it could not present. A refused damage swap now falls back to eglSwapBuffers and says so once. A driver without the extension presents through the plain call as before. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index e1ca00a3..3284812f 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -18289,13 +18289,25 @@ namespace MobileGL::MG_Backend::DirectGLES { if (const auto* entry = FindSessionSurfaceLocked(session, drawSurface)) windowSurface = entry->Window != static_cast(0); } + EGLBoolean mglSwapped = EGL_FALSE; if (g_EGLFuncs.eglSwapBuffersWithDamageEXT != nullptr) { const MG_Util::Damage::Region& damage = MG_Backend::CurrentPresentDamage(); EGLint rects[MG_Util::Damage::kMaxRects * 4] = {}; const EGLint count = static_cast(MG_Util::Damage::NativeSwapRects(windowSurface, damage, rects)); - g_EGLFuncs.eglSwapBuffersWithDamageEXT(g_Display, drawSurface, count > 0 ? rects : nullptr, count); - } else { - g_EGLFuncs.eglSwapBuffers(g_Display, drawSurface); + mglSwapped = g_EGLFuncs.eglSwapBuffersWithDamageEXT(g_Display, drawSurface, count > 0 ? rects : nullptr, count); + // A DRIVER THAT NAMES THE ENTRY POINT AND THEN REFUSES THE SWAP. Some vendor EGLs + // export eglSwapBuffersWithDamageEXT and answer EGL_FALSE on a window surface without + // setting an error (measured on a 4.14 vendor kernel with an Adreno driver; the plain + // entry point presents the same surface happily). Keeping the frame on that answer + // queues nothing at all: the compositor composites into a window whose buffer queue + // never sees a buffer. Fall back to the plain call instead of dropping the frame. + if (mglSwapped == EGL_FALSE && g_EGLFuncs.eglSwapBuffers != nullptr) { + MGLOG_W_ONCE("DirectGLES: this driver's eglSwapBuffersWithDamageEXT refused a window " + "surface; presenting through eglSwapBuffers for the rest of the process"); + mglSwapped = g_EGLFuncs.eglSwapBuffers(g_Display, drawSurface); + } + } else if (g_EGLFuncs.eglSwapBuffers != nullptr) { + mglSwapped = g_EGLFuncs.eglSwapBuffers(g_Display, drawSurface); } if (drawSurface != EGL_NO_SURFACE) { const std::lock_guard lock(g_nativeRegistryMutex); From b7a6e6c538d1c6c9c41c862ff06f507984b7226b Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sat, 3 Oct 2026 15:55:53 +0800 Subject: [PATCH 3/6] [Docs] (Anland): the D1100 + Mali, kernel 4.14 bring-up and its three fixes --- .../notes/anland/runbook-plasma-d1100-mali.md | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 docs/Disaggregated/notes/anland/runbook-plasma-d1100-mali.md diff --git a/docs/Disaggregated/notes/anland/runbook-plasma-d1100-mali.md b/docs/Disaggregated/notes/anland/runbook-plasma-d1100-mali.md new file mode 100644 index 00000000..577cf966 --- /dev/null +++ b/docs/Disaggregated/notes/anland/runbook-plasma-d1100-mali.md @@ -0,0 +1,185 @@ +# Runbook:D1100 + Mali、内核 4.14 的设备上起 MobileGL + anland + KDE Plasma(2026-10-03) + +> 第二台设备的实测记录。参考机(Lenovo Y700 / Adreno 750 / 内核 6.1 / KernelSU)见 +> [`runbook-plasma.md`](runbook-plasma.md);本文只写**与它不同的地方**、以及那台机器上不会 +> 遇到的坑。台面上的三个修复随分支 `feat/disaggregated-fix-d1100-mali` 提交:两个在 MobileGL +> 里(本仓库),两个在 anland 的 consumer app 里。 + +## 设备与容器(与参考机的全部差异) + +| 项 | 参考机 | 本机 | +|---|---|---| +| SoC / GPU | 骁龙 8 Gen 3 / **Adreno 750** | 天玑 1100 / **Mali**(droidspaces 镜像 `/dev/mali0`) | +| 内核 | 6.1 | **4.14.186**(+ cgroup v1) | +| root | KernelSU | **Magisk**(`su -c` 可用;`/data/adb/magisk`) | +| 系统 | Android 15 | **TrebleDroid GSI, Android 14 (SDK 34)** | +| adb | USB `HA27Q3LQ` | USB `aikjs4s8cys8ba7l` | +| 容器 | `arch-kde-mgl`(Arch,**systemd**) | `fedora-kde`(Fedora 44 Container Image,**无 systemd**) | +| 容器 init | `desktop-session.service` | `custom_init=/usr/local/sbin/ds-init.sh`(`exec setpriv … dbus-run-session -- startplasma-wayland`) | +| 桌面用户 | `swung0x48` | `Gold`(uid 1000) | +| 容器里的编译器 | 有(gcc/cmake/ninja) | **没有**(运行时镜像,且容器无 DNS) | +| `/dev/dri` | 有 renderD\* | **只有 `card0`(600 root:root)** | +| 容器截图/构建 | 容器内构建 client 与 KWin | **全部在主机(PC)交叉编译**,只把产物送进容器 | + +容器里 systemd 起不来的原因很直接(`/data/local/Droidspaces/Logs/fedora-kde/console`): + +``` +systemd 259.9-1.fc44 … Warning! Reported kernel version 4.14.186 is older than systemd's required +baseline kernel version 5.7. +Detected cgroup v1 hierarchy at /sys/fs/cgroup/, which is no longer supported… +[!!!!!!] Detected unsupported legacy cgroup hierarchy, refusing execution. +``` + +所以本机**没有** `desktop-session.service`/`plasma-kwin_wayland.service` 这条链路, +`mobilegl-startup.sh plasma` 里那几行 `systemctl --user` 也没有意义。等价物见下文"会话"。 + +## 一次性准备 + +1. **容器 bind**:`container.config` 的 `bind_mounts` 要有两条—— + `/data/local/tmp/display_daemon.sock:/run/display.sock` 与 + `/data/local/tmp/anland-mobilegl:/run/anland-mobilegl`(后者镜像里默认没有,缺了客户端就看不到 + daemon socket)。`net_mode=host` 必须有(`@anland-mobilegl` 是抽象 socket)。 + Android 侧 `mkdir -p /data/local/tmp/anland-mobilegl && chmod 777`。 +2. **client 库**(主机交叉编译):容器运行时是 glibc 2.43 / GLIBCXX 3.4.36,和 Fedora 44 aarch64 + sysroot 一致,所以用 `scripts/xsysroot/aarch64-arch.cmake` 的等价物(clang + lld + + `--gcc-install-dir=/usr/lib/gcc/aarch64-redhat-linux/16`)编 + `libMobileGL.so` + `mobilegl_gbm.so`,参数与 `xbuild.sh` 相同。**注意 64 位发行版的路径**: + `/usr/lib64/gbm/mobilegl_gbm.so`、`/usr/lib64/libGLX_mobilegl.so.0`(`xdeploy.sh` 写的是 Arch 的 + `/usr/lib`,两条都放最省事)。 +3. **KWin**:pristine `kwin-6.7.5`(`download.kde.org/stable/plasma/6.7.5/kwin-6.7.5.tar.xz`) + + `producers/kde/Arch_v5/kwin.patch` + `anland_backend_Arch_v5` + 顶层 `libdisplay_producer/` + (后者里的 `protocol.h`/`socket_utils.c` 是指向 `common/` 的符号链接,**拷的时候要 -L 解引用**, + 否则 configure 报 "Cannot find source file … socket_utils.c";也**不要**把 `common/` 整个拷进 + backend 目录,那会让 `kde_clang_format` 生成重名的 clang-format target 而 configure 失败)。 + 7.7.5 的树上 `kwin.patch` 只有 `src/opengl/eglcontext.cpp` 的 Hunk #2 打不上(6.7.5 里 + `makeCurrent()` 的上下文不同),按补丁原意手工改:no-arg `makeCurrent()` 里把 + `EGL_NO_SURFACE, EGL_NO_SURFACE` 换成 `m_display->defaultSurface()`。 + 装到 `/opt/mobilegl/kwin/{bin,lib}`,screencast 插件就地替换 + `/usr/lib64/qt6/plugins/kwin/plugins/screencast.so`(留 `.orig`)。 +4. **容器缺的运行时库**:交叉编出来的 KWin 需要 `libatomic.so.1`,这个运行时镜像没有。 + 从 sysroot 把 `libatomic.so.1.2.0` + 软链补进 rootfs(容器停着时直接写 + `/data/local/Droidspaces/fedora-kde/usr/lib64/` 最方便)。`ldd` 其余依赖 Qt6/KF6 都齐。 + +## 会话(无 systemd 的等价物) + +镜像原来的 `ds-init.sh` 用 **llvmpipe 软件渲染**起 Plasma,且 export 了 +`LIBGL_ALWAYS_SOFTWARE=1` —— MobileGL 对"要求软件渲染的进程"主动让位,所以那一行必须去掉。 +把这个脚本装到容器 `/opt/mobilegl/bin/start-mobilegl-session.sh`: + +```sh +# 复刻 mobilegl-startup.sh plasma 的 env(KWin 用 server,其余 client 用 offscreen), +# 然后 exec startplasma-wayland —— PATH 前置 /opt/mobilegl/kwin/bin, +# kwin_wayland_wrapper 就会 exec 我们的 KWin。 +export KWIN_BIN_DIR=/opt/mobilegl/kwin/bin KWIN_LIB_DIR=/opt/mobilegl/kwin/lib +export __EGL_VENDOR_LIBRARY_FILENAMES=/opt/mobilegl/share/glvnd/egl_vendor.d/50_mobilegl.json +export __GLX_VENDOR_LIBRARY_NAME=mobilegl GBM_BACKEND=mobilegl ANLAND_MOBILEGL=1 +export ANLAND_SOCKET=/run/anland-mobilegl/display.sock +export MOBILEGL_TRANSPORT=spawn MOBILEGL_IPC_DATA=shm MOBILEGL_IPC_CONTROL=unix:@anland-mobilegl +export MOBILEGL_IPC_SURFACE=server MOBILEGL_BACKEND_TYPE=$(cat /etc/mobilegl/backend) +export MOBILEGL_GBM_NODE=/dev/null MOBILEGL_DEVICE_DRM_NODE=/dev/null +export KWIN_DISABLE_VULKAN=1 KWIN_NO_TIMER_QUERY=1 KWIN_PERSISTENT_VBO=0 KWIN_DISABLE_UDMABUF_IMPORT=1 +export PATH="$KWIN_BIN_DIR:$PATH" LD_LIBRARY_PATH="$KWIN_LIB_DIR" +unset LIBGL_ALWAYS_SOFTWARE GALLIUM_DRIVER MESA_LOADER_DRIVER_OVERRIDE +exec /usr/bin/startplasma-wayland +``` + +`/dev/dri` 里没有 `renderD*` 时 `MOBILEGL_GBM_NODE` 取 `/dev/null`:那个节点只是身份, +后端从不对它 ioctl(`mobilegl-startup.sh` 自己也是这么退的),不需要改设备节点权限。 + +`ds-init.sh` 最后的 `exec … startplasma-wayland` 换成 `dbus-run-session -- /opt/mobilegl/bin/start-mobilegl-session.sh`, +并去掉 `LIBGL_ALWAYS_SOFTWARE`/`GALLIUM_DRIVER`。**会话在容器启动时就起**,所以 bring-up 顺序是 +**app → display daemon → 容器重启**(不是 runbook 里那条 systemd 顺序)。 + +## 本机独有的三个坑(都已修,随分支提交) + +### 1. 内核不保证 dma-buf inode 唯一 → 零拷贝被整体拒绝 + +参考机靠 `(st_dev, st_ino)` 认"这块 dma-buf 是不是 server 自己发的"。本机**所有** dma-buf +descriptor 报同一个身份: + +``` +bo0: fd=15 dev=12 ino=10658 size=0 fdinfo[pos: 0 flags: 02000000 mnt_id: 12 ] +bo1: fd=17 dev=12 ino=10658 size=0 … +bo2: fd=19 dev=12 ino=10658 size=0 … +``` + +`/proc/self/fdinfo` 里也没有 name / 任何唯一量,也就是用户态**拿不到**能区分 buffer 的东西。 +`SharedImageRegistry` 正确地检测到冲突并按设计 fail-closed: + +``` +[ERROR] two live shared images have one dma-buf identity (dev 12 ino 10658); this kernel does not + keep dma-buf inodes unique … +[WARN] a dma-buf import was refused: dma-buf identities are not unique on this kernel +``` + +后果是**所有** linux-dmabuf 窗口都被拒,client 全部退回 `wl_shm`(`8 presented through wl_shm`), +合成器也就没法采样它上不了屏的东西。 + +**修复**(`feat/disaggregated-fix-d1100-mali` 的第一个提交):身份仍然优先由内核给;不唯一时按 +**导入记录自己带的 extent + fourcc**,在该身份下已导出的活镜像里取"最早且未被认领"的那个 +(分配顺序 = 生产者交给合成器的顺序)。`Identify()` 多收 `width/height/fourcc` 三个参数, +`PipeApplier` 的导入分支本来就有它们。 + +修好后 client 日志变成验收项要的那句,且 refusal 归零: + +``` +8 presented through linux-dmabuf shared images +``` + +自包含复现(主机用 clang+lld 编,容器里以 Gold 跑):`eglprobe4.c`——`gbm_bo_create` 拿一块 +server shared image 的 dma-buf fd,再走 KWin 的导入路径 `eglCreateImageKHR(EGL_LINUX_DMA_BUF_EXT)`。 +修前 `-> (nil)`,修后 `-> 0x1`。 + +### 2. 驱动暴露 `eglSwapBuffersWithDamageEXT` 却对它返回 EGL_FALSE + +这条是"屏幕全黑"的最后一环。server 的 `Present()` 在两个入口里优先用 WithDamage 变体; +本机驱动(Mali / 4.14 vendor EGL)对**窗口面**返回 EGL_FALSE 且**不设 EGL 错误**: + +``` +server window: 1080x2194 fmt=0x1 (pointer=0xb4000070d902ad00) ← ANativeWindow 是活的 +swap entry points: withDamage=0x71a0887040 plain=0x71a087c62c +withDamage → swap=0 err=0x3000 ; plain → swap=1 err=0x3000 ← 同一块 surface、同一个线程 +``` + +surface 在当前线程上是 current、context 也是 current、窗口尺寸格式都对,所以不是绑定问题—— +就是驱动拒绝。信任那个指针等于**一帧都不入队**:SurfaceView 的 BLAST 层 `frame events` 全 0、 +`geomLayerBounds` 0×0,屏幕上只有 `Background for SurfaceView` 那块纯黑占位。 + +**修复**(第二个提交):damage 变体返回 EGL_FALSE 时回落普通 `eglSwapBuffers`,并 +`MGLOG_W_ONCE` 说明一次。修好后 `frame events` 出现真实时间戳,`screencap` 从 37 KB(纯黑) +变成 1.4 MB 的完整桌面。 + +### 3. app 侧两处(在 anland 仓库,不在本仓库) + +- **`:mobilegl` worker 从没设 `MOBILEGL_IPC_SURFACE=server`**。`IpcTable::Surface` 的默认是 + `Offscreen`,而 client 侧(会话脚本)按 `server` 去要 server 的窗口,于是 server **把窗口 + attach 了却从不创建 server-owned 窗口面**——日志里只有 + `a ServerOwned window surface is served on its window`,没有真正的 + `surface=window … owner=server`。在 `MobileGLWorker.java` 的 `MOBILEGL_IPC_ROLE` 旁边加一行 + `Os.setenv("MOBILEGL_IPC_SURFACE", "server", true)` 即可。 +- **`MobileGLConnection` 的几何回调**在收到 (0,0) 时会 `setSizeFromLayout()`;若此时视图还没 + 第一次布局,surface 会被定成 **0×0**,之后任何生产者都无法 queue。改成"视图确实有尺寸时才 + 回到布局"。 + +## 验收(本机实测结论) + +- 会话:`kwin_wayland`(`/proc//exe -> /opt/mobilegl/kwin/bin/kwin_wayland`)+ `plasmashell` + 常驻;client 日志 `Renderer Name: Espryt`、`CapsMirror generation N adopted`、 + `P65ClientPace present=N`;server 日志 `P65ServerFrame frame=N`(空闲桌面约 1 帧/分钟, + 只在有 damage 时合成)。 +- 上屏:`screencap` 得到 1080×2400 的完整 Plasma 桌面(壁纸 + 面板 + 时钟),不再是纯黑。 + SurfaceView 的 BLAST 层有真实 `frame events`。 +- 验收项 `presented through linux-dmabuf shared images` **达成**(8/8,0 refusal)。 + +## 仍未解决 / 未验证 + +- **daemon 的 producer/consumer 每 ~5 s 抖一次**(`daemon: producer connected/disconnected`、 + `consumer re-deposited 5 fds`,kwin 侧 `kwin_backend_anland: consumer disconnected, entering + fallback`)。在 MobileGL 模式下合成不受影响(窗口和默认 framebuffer 属于 KWin),所以桌面 + 照常上屏,但这条抖动本身的成因没查。 +- 桌面静止时两次截图 md5 相同(时钟只到分钟、无动画),要证明"活着"看 server 的 frame 计数 + 或 `testImportBuffer`;参考机的 `shot.sh` 两帧哈希法在这台机器上需要先制造 damage。 +- **Magma(DirectVulkan)未复验**:`setprop debug.mobilegl.backend DirectVulkan` + + `/etc/mobilegl/backend=DirectVulkan` 后重启 app 与容器即可。上面三个坑与后端无关, + 两个后端都会撞到(都已在 DirectGLES 上验证过修复路径)。 +- 容器无 DNS、无编译器,所以"容器内构建"这条上游路线在本机不适用;本机全部交叉编译。 From 0d89db36dd1dcee0d8d906b55586d46e25abf308 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sat, 3 Oct 2026 15:57:11 +0800 Subject: [PATCH 4/6] [Docs] (Anland): the D1100 + Mali changes and why each one was needed --- .../notes/anland/changes-for-d1100-mali.md | 153 +++++++++++++++ .../notes/anland/runbook-plasma-d1100-mali.md | 185 ------------------ 2 files changed, 153 insertions(+), 185 deletions(-) create mode 100644 docs/Disaggregated/notes/anland/changes-for-d1100-mali.md delete mode 100644 docs/Disaggregated/notes/anland/runbook-plasma-d1100-mali.md diff --git a/docs/Disaggregated/notes/anland/changes-for-d1100-mali.md b/docs/Disaggregated/notes/anland/changes-for-d1100-mali.md new file mode 100644 index 00000000..f7922960 --- /dev/null +++ b/docs/Disaggregated/notes/anland/changes-for-d1100-mali.md @@ -0,0 +1,153 @@ +# 为了在天玑 1100 + Mali(内核 4.14)上跑起来,改了什么、为什么 + +> 本文只记**改动与动机**。参考机(Lenovo Y700 / Adreno 750 / 内核 6.1 / KernelSU / 有 systemd 的 +> Arch 容器)的完整操作手册见 [`runbook-plasma.md`](runbook-plasma.md);本文的改动都在分支 +> `feat/disaggregated-fix-d1100-mali` 上,两个在 MobileGL 里(本仓库),两个在 anland 的 +> consumer app 里。 +> +> 判据:改之前 `screencap` 是 37 KB 的纯黑;改之后是 1.4 MB 的完整 Plasma 桌面 +> (壁纸 + 面板 + 时钟),client 日志的验收项从 `presented through wl_shm` 变成 +> `presented through linux-dmabuf shared images`。 + +## 这台机器与参考机的差异(每条改动都从这里来) + +| 项 | 参考机 | 本机 | +|---|---|---| +| GPU / 驱动 | Adreno 750 | **Mali**(`/dev/mali0`) | +| 内核 | 6.1 | **4.14.186**(cgroup v1) | +| 容器 | Arch + **systemd** | Fedora 44,**无 systemd**(`custom_init=ds-init.sh`) | +| 容器里的编译器 | 有 | **没有**,且无 DNS → 全部主机交叉编译 | +| `/dev/dri` | 有 `renderD*` | 只有 `card0` | + +--- + +# MobileGL 仓库(本分支) + +## 改动 1:dma-buf 身份认不出来时,用导入自己的 extent + format 认领 + +**文件**:`MobileGL/MG_Remote/Server/SharedImageRegistry.{h,cpp}`、`MobileGL/MG_Remote/Server/PipeApplier.cpp` + +**改了什么**:`Identify(int fd, …)` 增加 `width/height/fourcc` 三个参数(导入记录本来就有它们)。 +内核身份 `(st_dev, st_ino)` 仍然优先;只有当注册表已经发现身份**不唯一**时,才退化为:在该身份 +下已导出的活镜像里,挑 **extent 与 fourcc 都相同、且尚未被认领** 的那个,按 `Id` 从小到大 +(= 分配顺序 = 生产者交给合成器的顺序),认领一次。 + +**为什么必须改**:零拷贝的整条链是"server 分配 AHardwareBuffer → 导出 dma-buf fd → client 把它做成 +`wl_buffer` → 合成器 `eglCreateImageKHR` 导回 → server 认出是自己那块"。认的依据只有 +`(st_dev, st_ino)`。本机内核给**所有** dma-buf descriptor 发同一个身份(实测三个不同的 buffer): + +``` +bo0: fd=15 dev=12 ino=10658 size=0 fdinfo[pos: 0 flags: 02000000 mnt_id: 12 ] +bo1: fd=17 dev=12 ino=10658 size=0 … +bo2: fd=19 dev=12 ino=10658 size=0 … +``` + +`fdinfo` 里没有 name、没有别的唯一量——用户态拿不到任何能区分 buffer 的东西。注册表检测到 +"两块活镜像同一个身份"后**按设计 fail-closed**: + +``` +[ERROR] two live shared images have one dma-buf identity (dev 12 ino 10658); this kernel does not + keep dma-buf inodes unique … +[WARN] a dma-buf import was refused: dma-buf identities are not unique on this kernel +``` + +于是**每一个** linux-dmabuf 窗口都被拒:client 全退回 `wl_shm`(`8 presented through wl_shm`), +合成器也没法采样它上不了屏的东西。fail-closed 本身是对的(猜错会把别人的像素当自己的), +缺的只是**内核给不出身份时还有别的可用依据**——导入声明的尺寸和格式,加上 server 自己的分配顺序。 + +**验证**:自包含探针 `eglprobe4.c`(主机 clang+lld 编,容器里以 Gold 跑)——`gbm_bo_create` +取一块 server shared image 的 fd,再走合成器的导入路径:修前 +`eglCreateImageKHR(EGL_LINUX_DMA_BUF_EXT) -> (nil)`,修后 `-> 0x1`;会话里 +refusal 归零、client 日志变成 `presented through linux-dmabuf shared images`。 + +## 改动 2:damage 变体被驱动拒绝时,回落普通 `eglSwapBuffers` + +**文件**:`MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp` 的 `Present()` + +**改了什么**:`eglSwapBuffersWithDamageEXT` 返回 EGL_FALSE 时,用 `eglSwapBuffers` 再试一次, +并 `MGLOG_W_ONCE` 说明一次(没有该扩展的驱动照旧走普通入口)。 + +**为什么必须改**:server 的 `Present()` 原本优先用 WithDamage 变体(注释说"驱动提供哪个拼写就用 +哪个")。本机的 Mali/4.14 vendor EGL **暴露了这个入口,却对窗口面返回 EGL_FALSE 且不设 EGL 错误**: + +``` +server window: 1080x2194 fmt=0x1 (pointer=0xb4000070d902ad00) ← ANativeWindow 是活的、尺寸对 +swap entry points: withDamage=0x71a0887040 plain=0x71a087c62c +withDamage → swap=0 err=0x3000 ← 同一块 surface、同一线程 +plain → swap=1 err=0x3000 +``` + +排除了绑定问题(`session.Draw == eglGetCurrentSurface(EGL_DRAW)`、context 也 current),也不是窗口 +问题(`ANativeWindow_getWidth/Height/Format` 都正常)——就是驱动拒。信任那个指针的后果是 +**一帧都不入队**:SurfaceView 的 BLAST 层 `frame events` 全 0、`geomLayerBounds` 0×0,屏幕上只有 +`Background for SurfaceView` 那块纯黑占位;合成器又能采样但因为上不了屏,client 也跟着退回 +`wl_shm`。这条是"屏幕全黑"的最后一环。 + +**验证**:修后 `frame events` 出现真实时间戳,`screencap` 从 37 KB 变 1.4 MB 的完整桌面。 + +--- + +# anland 仓库(consumer app) + +这两条不在本仓库里,但缺任何一条桌面都上不了屏。 + +## 改动 3:`:mobilegl` worker 设置 `MOBILEGL_IPC_SURFACE=server` + +**文件**:`consumers/anland_v5/android_consumer/app/src/main/java/com/anland/consumer/MobileGLWorker.java` + +**改了什么**:在 `Os.setenv("MOBILEGL_IPC_ROLE", "server", …)` 旁边加 +`Os.setenv("MOBILEGL_IPC_SURFACE", "server", true)`。 + +**为什么必须改**:`Config.h` 里 `IpcTable::Surface` 的默认值是 `IpcSurface::Offscreen`。 +client 侧(容器里的 `mobilegl-startup.sh`/`start-mobilegl-session.sh`)按 `server` 去要 +server 的窗口,而 **server 自己的进程没有任何 `MOBILEGL_IPC_SURFACE`**,于是它把 app 递过来的 +窗口 attach 了、却**从不创建 server-owned 窗口面、也从不往上面 swap**。日志上表现为只有一句 +`a ServerOwned window surface is served on its window`,没有真正的 +`surface=window … owner=server`。补上之后这一行立刻出现。 + +## 改动 4:几何回调不再把 Surface 塌成 0×0 + +**文件**:`…/MainActivity.java`(`MobileGLConnection` 的几何回调) + +**改了什么**:回调收到 `(0,0)`("不指定尺寸")时,只有在视图**确实已有尺寸**时才 +`holder.setSizeFromLayout()`;否则什么都不做,保留现有 surface。 + +**为什么必须改**:这个回调可能在视图第一次布局之前被调用,此时 `setSizeFromLayout()` 会把 +`SurfaceHolder` 定成 0×0;0×0 的 surface 上任何生产者都无法 queue,之后每一帧 +`eglSwapBuffers` 都会失败——正是"BLAST 层 `geomLayerBounds` 0×0 且永远没有 buffer"的成因。 + +--- + +# 环境侧(不是代码,但缺了就起不来) + +1. **`libatomic.so.1`**:主机交叉编出来的 KWin 需要它,而这个 Fedora 运行时镜像没有 + (`kwin_wayland: error while loading shared libraries: libatomic.so.1`)。从 sysroot 把 + `libatomic.so.1.2.0` + 软链补进 rootfs(容器停着时直接写 + `/data/local/Droidspaces/fedora-kde/usr/lib64/`)。`ldd` 其余 Qt6/KF6 依赖都齐。 +2. **无 systemd 的会话**:容器里 systemd 259 因"内核 4.14 + cgroup v1"直接拒绝启动,会话由 + `ds-init.sh` 自己拉起。等价物是把 `mobilegl-startup.sh plasma` 的那套 env 写进 + `/opt/mobilegl/bin/start-mobilegl-session.sh`(PATH 前置 `/opt/mobilegl/kwin/bin` 让 + `kwin_wayland_wrapper` 起我们的 KWin),再让 `ds-init.sh` 的最后一句 exec 它。**同时必须去掉 + `LIBGL_ALWAYS_SOFTWARE=1`/`GALLIUM_DRIVER=llvmpipe`**:MobileGL 对"要求软件渲染的进程"主动 + 让位,留着它整个会话就还是 llvmpipe。 +3. **GBM 后端的路径**:Fedora 的 loader 搜 `/usr/lib64/gbm`(上游脚本写的是 Arch 的 + `/usr/lib/gbm`);`libGLX_mobilegl.so.0` 同理放 `/usr/lib64`。 +4. **`/dev/dri` 只有 `card0`**:`MOBILEGL_GBM_NODE` 退到 `/dev/null` —— 那个节点只当身份、 + 后端从不对它 ioctl,这是 `mobilegl-startup.sh` 自己的回退路径,不需要改设备节点权限。 +5. **KWin 的构建**:pristine `kwin-6.7.5` + `Arch_v5/kwin.patch` + `anland_backend_Arch_v5` + + 顶层 `libdisplay_producer/`(后者的 `protocol.h`/`socket_utils.c` 是指向 `common/` 的符号链接, + **拷贝时要 -L 解引用**;也**不要**把 `common/` 整个拷进 backend 目录,会让 `kde_clang_format` + 生成重名 target)。6.7.5 上只有 `src/opengl/eglcontext.cpp` 的 Hunk #2 打不上,按补丁原意手工把 + no-arg `makeCurrent()` 里的 `EGL_NO_SURFACE, EGL_NO_SURFACE` 换成 + `m_display->defaultSurface()`。 + +--- + +# 已知但未处理 + +- daemon 的 producer/consumer 每 ~5 s 抖一次(`producer connected/disconnected`、 + `consumer re-deposited 5 fds`,kwin 侧 `consumer disconnected, entering fallback`)。MobileGL + 模式下合成不受影响(窗口与默认 framebuffer 属于 KWin),桌面照常上屏,成因未查。 +- 桌面静止时两次 `screencap` 的 md5 相同(时钟只到分钟、无动画);判断"活着"要看 server 的 + `P65ServerFrame` 计数或 SurfaceView 的 frame events。 +- **Magma(DirectVulkan)未复验**。上面三条代码改动都与后端无关,两个后端都会撞到。 diff --git a/docs/Disaggregated/notes/anland/runbook-plasma-d1100-mali.md b/docs/Disaggregated/notes/anland/runbook-plasma-d1100-mali.md deleted file mode 100644 index 577cf966..00000000 --- a/docs/Disaggregated/notes/anland/runbook-plasma-d1100-mali.md +++ /dev/null @@ -1,185 +0,0 @@ -# Runbook:D1100 + Mali、内核 4.14 的设备上起 MobileGL + anland + KDE Plasma(2026-10-03) - -> 第二台设备的实测记录。参考机(Lenovo Y700 / Adreno 750 / 内核 6.1 / KernelSU)见 -> [`runbook-plasma.md`](runbook-plasma.md);本文只写**与它不同的地方**、以及那台机器上不会 -> 遇到的坑。台面上的三个修复随分支 `feat/disaggregated-fix-d1100-mali` 提交:两个在 MobileGL -> 里(本仓库),两个在 anland 的 consumer app 里。 - -## 设备与容器(与参考机的全部差异) - -| 项 | 参考机 | 本机 | -|---|---|---| -| SoC / GPU | 骁龙 8 Gen 3 / **Adreno 750** | 天玑 1100 / **Mali**(droidspaces 镜像 `/dev/mali0`) | -| 内核 | 6.1 | **4.14.186**(+ cgroup v1) | -| root | KernelSU | **Magisk**(`su -c` 可用;`/data/adb/magisk`) | -| 系统 | Android 15 | **TrebleDroid GSI, Android 14 (SDK 34)** | -| adb | USB `HA27Q3LQ` | USB `aikjs4s8cys8ba7l` | -| 容器 | `arch-kde-mgl`(Arch,**systemd**) | `fedora-kde`(Fedora 44 Container Image,**无 systemd**) | -| 容器 init | `desktop-session.service` | `custom_init=/usr/local/sbin/ds-init.sh`(`exec setpriv … dbus-run-session -- startplasma-wayland`) | -| 桌面用户 | `swung0x48` | `Gold`(uid 1000) | -| 容器里的编译器 | 有(gcc/cmake/ninja) | **没有**(运行时镜像,且容器无 DNS) | -| `/dev/dri` | 有 renderD\* | **只有 `card0`(600 root:root)** | -| 容器截图/构建 | 容器内构建 client 与 KWin | **全部在主机(PC)交叉编译**,只把产物送进容器 | - -容器里 systemd 起不来的原因很直接(`/data/local/Droidspaces/Logs/fedora-kde/console`): - -``` -systemd 259.9-1.fc44 … Warning! Reported kernel version 4.14.186 is older than systemd's required -baseline kernel version 5.7. -Detected cgroup v1 hierarchy at /sys/fs/cgroup/, which is no longer supported… -[!!!!!!] Detected unsupported legacy cgroup hierarchy, refusing execution. -``` - -所以本机**没有** `desktop-session.service`/`plasma-kwin_wayland.service` 这条链路, -`mobilegl-startup.sh plasma` 里那几行 `systemctl --user` 也没有意义。等价物见下文"会话"。 - -## 一次性准备 - -1. **容器 bind**:`container.config` 的 `bind_mounts` 要有两条—— - `/data/local/tmp/display_daemon.sock:/run/display.sock` 与 - `/data/local/tmp/anland-mobilegl:/run/anland-mobilegl`(后者镜像里默认没有,缺了客户端就看不到 - daemon socket)。`net_mode=host` 必须有(`@anland-mobilegl` 是抽象 socket)。 - Android 侧 `mkdir -p /data/local/tmp/anland-mobilegl && chmod 777`。 -2. **client 库**(主机交叉编译):容器运行时是 glibc 2.43 / GLIBCXX 3.4.36,和 Fedora 44 aarch64 - sysroot 一致,所以用 `scripts/xsysroot/aarch64-arch.cmake` 的等价物(clang + lld + - `--gcc-install-dir=/usr/lib/gcc/aarch64-redhat-linux/16`)编 - `libMobileGL.so` + `mobilegl_gbm.so`,参数与 `xbuild.sh` 相同。**注意 64 位发行版的路径**: - `/usr/lib64/gbm/mobilegl_gbm.so`、`/usr/lib64/libGLX_mobilegl.so.0`(`xdeploy.sh` 写的是 Arch 的 - `/usr/lib`,两条都放最省事)。 -3. **KWin**:pristine `kwin-6.7.5`(`download.kde.org/stable/plasma/6.7.5/kwin-6.7.5.tar.xz`) - + `producers/kde/Arch_v5/kwin.patch` + `anland_backend_Arch_v5` + 顶层 `libdisplay_producer/` - (后者里的 `protocol.h`/`socket_utils.c` 是指向 `common/` 的符号链接,**拷的时候要 -L 解引用**, - 否则 configure 报 "Cannot find source file … socket_utils.c";也**不要**把 `common/` 整个拷进 - backend 目录,那会让 `kde_clang_format` 生成重名的 clang-format target 而 configure 失败)。 - 7.7.5 的树上 `kwin.patch` 只有 `src/opengl/eglcontext.cpp` 的 Hunk #2 打不上(6.7.5 里 - `makeCurrent()` 的上下文不同),按补丁原意手工改:no-arg `makeCurrent()` 里把 - `EGL_NO_SURFACE, EGL_NO_SURFACE` 换成 `m_display->defaultSurface()`。 - 装到 `/opt/mobilegl/kwin/{bin,lib}`,screencast 插件就地替换 - `/usr/lib64/qt6/plugins/kwin/plugins/screencast.so`(留 `.orig`)。 -4. **容器缺的运行时库**:交叉编出来的 KWin 需要 `libatomic.so.1`,这个运行时镜像没有。 - 从 sysroot 把 `libatomic.so.1.2.0` + 软链补进 rootfs(容器停着时直接写 - `/data/local/Droidspaces/fedora-kde/usr/lib64/` 最方便)。`ldd` 其余依赖 Qt6/KF6 都齐。 - -## 会话(无 systemd 的等价物) - -镜像原来的 `ds-init.sh` 用 **llvmpipe 软件渲染**起 Plasma,且 export 了 -`LIBGL_ALWAYS_SOFTWARE=1` —— MobileGL 对"要求软件渲染的进程"主动让位,所以那一行必须去掉。 -把这个脚本装到容器 `/opt/mobilegl/bin/start-mobilegl-session.sh`: - -```sh -# 复刻 mobilegl-startup.sh plasma 的 env(KWin 用 server,其余 client 用 offscreen), -# 然后 exec startplasma-wayland —— PATH 前置 /opt/mobilegl/kwin/bin, -# kwin_wayland_wrapper 就会 exec 我们的 KWin。 -export KWIN_BIN_DIR=/opt/mobilegl/kwin/bin KWIN_LIB_DIR=/opt/mobilegl/kwin/lib -export __EGL_VENDOR_LIBRARY_FILENAMES=/opt/mobilegl/share/glvnd/egl_vendor.d/50_mobilegl.json -export __GLX_VENDOR_LIBRARY_NAME=mobilegl GBM_BACKEND=mobilegl ANLAND_MOBILEGL=1 -export ANLAND_SOCKET=/run/anland-mobilegl/display.sock -export MOBILEGL_TRANSPORT=spawn MOBILEGL_IPC_DATA=shm MOBILEGL_IPC_CONTROL=unix:@anland-mobilegl -export MOBILEGL_IPC_SURFACE=server MOBILEGL_BACKEND_TYPE=$(cat /etc/mobilegl/backend) -export MOBILEGL_GBM_NODE=/dev/null MOBILEGL_DEVICE_DRM_NODE=/dev/null -export KWIN_DISABLE_VULKAN=1 KWIN_NO_TIMER_QUERY=1 KWIN_PERSISTENT_VBO=0 KWIN_DISABLE_UDMABUF_IMPORT=1 -export PATH="$KWIN_BIN_DIR:$PATH" LD_LIBRARY_PATH="$KWIN_LIB_DIR" -unset LIBGL_ALWAYS_SOFTWARE GALLIUM_DRIVER MESA_LOADER_DRIVER_OVERRIDE -exec /usr/bin/startplasma-wayland -``` - -`/dev/dri` 里没有 `renderD*` 时 `MOBILEGL_GBM_NODE` 取 `/dev/null`:那个节点只是身份, -后端从不对它 ioctl(`mobilegl-startup.sh` 自己也是这么退的),不需要改设备节点权限。 - -`ds-init.sh` 最后的 `exec … startplasma-wayland` 换成 `dbus-run-session -- /opt/mobilegl/bin/start-mobilegl-session.sh`, -并去掉 `LIBGL_ALWAYS_SOFTWARE`/`GALLIUM_DRIVER`。**会话在容器启动时就起**,所以 bring-up 顺序是 -**app → display daemon → 容器重启**(不是 runbook 里那条 systemd 顺序)。 - -## 本机独有的三个坑(都已修,随分支提交) - -### 1. 内核不保证 dma-buf inode 唯一 → 零拷贝被整体拒绝 - -参考机靠 `(st_dev, st_ino)` 认"这块 dma-buf 是不是 server 自己发的"。本机**所有** dma-buf -descriptor 报同一个身份: - -``` -bo0: fd=15 dev=12 ino=10658 size=0 fdinfo[pos: 0 flags: 02000000 mnt_id: 12 ] -bo1: fd=17 dev=12 ino=10658 size=0 … -bo2: fd=19 dev=12 ino=10658 size=0 … -``` - -`/proc/self/fdinfo` 里也没有 name / 任何唯一量,也就是用户态**拿不到**能区分 buffer 的东西。 -`SharedImageRegistry` 正确地检测到冲突并按设计 fail-closed: - -``` -[ERROR] two live shared images have one dma-buf identity (dev 12 ino 10658); this kernel does not - keep dma-buf inodes unique … -[WARN] a dma-buf import was refused: dma-buf identities are not unique on this kernel -``` - -后果是**所有** linux-dmabuf 窗口都被拒,client 全部退回 `wl_shm`(`8 presented through wl_shm`), -合成器也就没法采样它上不了屏的东西。 - -**修复**(`feat/disaggregated-fix-d1100-mali` 的第一个提交):身份仍然优先由内核给;不唯一时按 -**导入记录自己带的 extent + fourcc**,在该身份下已导出的活镜像里取"最早且未被认领"的那个 -(分配顺序 = 生产者交给合成器的顺序)。`Identify()` 多收 `width/height/fourcc` 三个参数, -`PipeApplier` 的导入分支本来就有它们。 - -修好后 client 日志变成验收项要的那句,且 refusal 归零: - -``` -8 presented through linux-dmabuf shared images -``` - -自包含复现(主机用 clang+lld 编,容器里以 Gold 跑):`eglprobe4.c`——`gbm_bo_create` 拿一块 -server shared image 的 dma-buf fd,再走 KWin 的导入路径 `eglCreateImageKHR(EGL_LINUX_DMA_BUF_EXT)`。 -修前 `-> (nil)`,修后 `-> 0x1`。 - -### 2. 驱动暴露 `eglSwapBuffersWithDamageEXT` 却对它返回 EGL_FALSE - -这条是"屏幕全黑"的最后一环。server 的 `Present()` 在两个入口里优先用 WithDamage 变体; -本机驱动(Mali / 4.14 vendor EGL)对**窗口面**返回 EGL_FALSE 且**不设 EGL 错误**: - -``` -server window: 1080x2194 fmt=0x1 (pointer=0xb4000070d902ad00) ← ANativeWindow 是活的 -swap entry points: withDamage=0x71a0887040 plain=0x71a087c62c -withDamage → swap=0 err=0x3000 ; plain → swap=1 err=0x3000 ← 同一块 surface、同一个线程 -``` - -surface 在当前线程上是 current、context 也是 current、窗口尺寸格式都对,所以不是绑定问题—— -就是驱动拒绝。信任那个指针等于**一帧都不入队**:SurfaceView 的 BLAST 层 `frame events` 全 0、 -`geomLayerBounds` 0×0,屏幕上只有 `Background for SurfaceView` 那块纯黑占位。 - -**修复**(第二个提交):damage 变体返回 EGL_FALSE 时回落普通 `eglSwapBuffers`,并 -`MGLOG_W_ONCE` 说明一次。修好后 `frame events` 出现真实时间戳,`screencap` 从 37 KB(纯黑) -变成 1.4 MB 的完整桌面。 - -### 3. app 侧两处(在 anland 仓库,不在本仓库) - -- **`:mobilegl` worker 从没设 `MOBILEGL_IPC_SURFACE=server`**。`IpcTable::Surface` 的默认是 - `Offscreen`,而 client 侧(会话脚本)按 `server` 去要 server 的窗口,于是 server **把窗口 - attach 了却从不创建 server-owned 窗口面**——日志里只有 - `a ServerOwned window surface is served on its window`,没有真正的 - `surface=window … owner=server`。在 `MobileGLWorker.java` 的 `MOBILEGL_IPC_ROLE` 旁边加一行 - `Os.setenv("MOBILEGL_IPC_SURFACE", "server", true)` 即可。 -- **`MobileGLConnection` 的几何回调**在收到 (0,0) 时会 `setSizeFromLayout()`;若此时视图还没 - 第一次布局,surface 会被定成 **0×0**,之后任何生产者都无法 queue。改成"视图确实有尺寸时才 - 回到布局"。 - -## 验收(本机实测结论) - -- 会话:`kwin_wayland`(`/proc//exe -> /opt/mobilegl/kwin/bin/kwin_wayland`)+ `plasmashell` - 常驻;client 日志 `Renderer Name: Espryt`、`CapsMirror generation N adopted`、 - `P65ClientPace present=N`;server 日志 `P65ServerFrame frame=N`(空闲桌面约 1 帧/分钟, - 只在有 damage 时合成)。 -- 上屏:`screencap` 得到 1080×2400 的完整 Plasma 桌面(壁纸 + 面板 + 时钟),不再是纯黑。 - SurfaceView 的 BLAST 层有真实 `frame events`。 -- 验收项 `presented through linux-dmabuf shared images` **达成**(8/8,0 refusal)。 - -## 仍未解决 / 未验证 - -- **daemon 的 producer/consumer 每 ~5 s 抖一次**(`daemon: producer connected/disconnected`、 - `consumer re-deposited 5 fds`,kwin 侧 `kwin_backend_anland: consumer disconnected, entering - fallback`)。在 MobileGL 模式下合成不受影响(窗口和默认 framebuffer 属于 KWin),所以桌面 - 照常上屏,但这条抖动本身的成因没查。 -- 桌面静止时两次截图 md5 相同(时钟只到分钟、无动画),要证明"活着"看 server 的 frame 计数 - 或 `testImportBuffer`;参考机的 `shot.sh` 两帧哈希法在这台机器上需要先制造 damage。 -- **Magma(DirectVulkan)未复验**:`setprop debug.mobilegl.backend DirectVulkan` + - `/etc/mobilegl/backend=DirectVulkan` 后重启 app 与容器即可。上面三个坑与后端无关, - 两个后端都会撞到(都已在 DirectGLES 上验证过修复路径)。 -- 容器无 DNS、无编译器,所以"容器内构建"这条上游路线在本机不适用;本机全部交叉编译。 From a6d788191fb4cfc38572ddadcb4982d8d1f0cbba Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sat, 3 Oct 2026 16:07:53 +0800 Subject: [PATCH 5/6] [Docs] (Anland): record the Magma verification and its missing glyphs --- .../Disaggregated/notes/anland/changes-for-d1100-mali.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/Disaggregated/notes/anland/changes-for-d1100-mali.md b/docs/Disaggregated/notes/anland/changes-for-d1100-mali.md index f7922960..1d773950 100644 --- a/docs/Disaggregated/notes/anland/changes-for-d1100-mali.md +++ b/docs/Disaggregated/notes/anland/changes-for-d1100-mali.md @@ -150,4 +150,11 @@ server 的窗口,而 **server 自己的进程没有任何 `MOBILEGL_IPC_SURFAC 模式下合成不受影响(窗口与默认 framebuffer 属于 KWin),桌面照常上屏,成因未查。 - 桌面静止时两次 `screencap` 的 md5 相同(时钟只到分钟、无动画);判断"活着"要看 server 的 `P65ServerFrame` 计数或 SurfaceView 的 frame events。 -- **Magma(DirectVulkan)未复验**。上面三条代码改动都与后端无关,两个后端都会撞到。 +- **Magma(DirectVulkan)已复验:桌面能上屏**(`Renderer Name: Magma`、`8 presented through + linux-dmabuf`、SurfaceView frame events 是真实时间戳、`screencap` 1.05 MB 的桌面 + 面板 + 光标)。 + 上面三条代码改动都与后端无关,两个后端都撞到同一个坑,也都因此被修好。 +- **Magma 少画字**:同一场景下屏幕键盘的按键是**空白白块**(没有 q/w/e…、"American English"、 + 面板时钟的文字),而 Espryt 下这些字都在。看起来是 Magma 的字形/图集路径在这台 Mali 驱动上 + 没画出来,与本文的改动无关,**未查**。首次复现:把后端切到 DirectVulkan(`setprop + debug.mobilegl.backend DirectVulkan` + `/etc/mobilegl/backend`)重启 app 与容器,截屏看 + `plasma-keyboard`。 From 8302dd30bfb55a9ff5241a010b9cfdd999057a11 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sat, 3 Oct 2026 16:34:25 +0800 Subject: [PATCH 6/6] [Docs] (Anland): the lock screen behind the black desktop, and where the Magma text gap is not --- .../notes/anland/changes-for-d1100-mali.md | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/Disaggregated/notes/anland/changes-for-d1100-mali.md b/docs/Disaggregated/notes/anland/changes-for-d1100-mali.md index 1d773950..86cf0238 100644 --- a/docs/Disaggregated/notes/anland/changes-for-d1100-mali.md +++ b/docs/Disaggregated/notes/anland/changes-for-d1100-mali.md @@ -150,11 +150,25 @@ server 的窗口,而 **server 自己的进程没有任何 `MOBILEGL_IPC_SURFAC 模式下合成不受影响(窗口与默认 framebuffer 属于 KWin),桌面照常上屏,成因未查。 - 桌面静止时两次 `screencap` 的 md5 相同(时钟只到分钟、无动画);判断"活着"要看 server 的 `P65ServerFrame` 计数或 SurfaceView 的 frame events。 +- **"桌面全黑"里有一半是锁屏**:会话跑一会儿后 kscreenlocker 会把屏幕锁上,看到的黑屏其实是 + greeter(它自己也打 `Renderer Name: …`,所以日志里会误以为是桌面的渲染器)。判断方法: + `pgrep -a -f kscreenlocker_greet`。修法:写 `~/.config/kscreenlockerrc` + (`[Daemon] Autolock=false`/`LockOnStart=false`)后重启会话;**杀 greeter 没用**,kwin 会用 + `--immediateLock` 再起一个,D-Bus 的 `SetActive(false)` 也解不开。这一条是"看不到完整桌面" + 的直接原因,与渲染无关。 - **Magma(DirectVulkan)已复验:桌面能上屏**(`Renderer Name: Magma`、`8 presented through linux-dmabuf`、SurfaceView frame events 是真实时间戳、`screencap` 1.05 MB 的桌面 + 面板 + 光标)。 上面三条代码改动都与后端无关,两个后端都撞到同一个坑,也都因此被修好。 -- **Magma 少画字**:同一场景下屏幕键盘的按键是**空白白块**(没有 q/w/e…、"American English"、 - 面板时钟的文字),而 Espryt 下这些字都在。看起来是 Magma 的字形/图集路径在这台 Mali 驱动上 - 没画出来,与本文的改动无关,**未查**。首次复现:把后端切到 DirectVulkan(`setprop - debug.mobilegl.backend DirectVulkan` + `/etc/mobilegl/backend`)重启 app 与容器,截屏看 - `plasma-keyboard`。 +- **Magma 少画字(未修,已缩小到一条线索)**:同一场景下所有 `Text` 都不画(键盘按键是空白白块、 + 没有 q/w/e…/"American English",面板时钟也没有),而矩形/图标/壁纸都正常。已用探针**逐条排除**: + `eglprobe6/8`(ES2 的 `GL_ALPHA` 与 ES3 的 `GL_R8`/`GL_RED` 采样 `.a`/`.r` 均正确)、 + `eglprobe7`(`UNPACK_ALIGNMENT=1` 的窄行上传正确)、`eglprobe9`(图集模式:大纹理 + + `glTexSubImage2D` 偏移 + `GL_UNPACK_ROW_LENGTH`,值全对、未触碰区域仍为 0)、 + `eglprobe10`(`fwidth` 与距离场 `smoothstep` 都正常)。 + 剩下的一手证据在容器日志里:**Magma 下 Qt 的 RHI 上下文建立失败**—— + `QRhiGles2: Failed to make context current. Expect bad things to happen.` 与 + `QWaylandGLContext::makeCurrent: eglError: 0x3002`(EGL_NOT_INITIALIZED),Espryt 下没有这两条。 + 下一步应从 `DirectVulkan` 的 `MakeEGLCurrent`/`OnClientContextBound`(`m_contextBindings` + 按上下文找 surface 的那条路,注释里明确写着"Qt 每个窗口一个线程与上下文")查起。 + 顺带记一个可查项:Magma 的 `GL_EXTENSIONS` **不含 `OES_standard_derivatives`**,而 `fwidth` + 实际可用。