feat(termux): glibc 构建自动启用自编 Mesa 26 turnip ICD(GPU 打通)

- app/boot/termux_gpu:glibc 构建 + Termux 且随附 ICD 存在时,在探测前设
  VK_ICD_FILENAMES/VK_DRIVER_FILES;用户显式设置不覆盖
- 文档:Mesa 26.2.6 + KGSL 的完整构建/安装/验证步骤(含 shebang、
  --wrap-mode=nofallback、C++/链接参数三处坑)
- 实测:wgpu 选中 Adreno 840(turnip Mesa 26.2.6),截图首帧 61 ms
- README.termux:GPU 标 ✅,MC 标 🟡(glibc JRE+AWT 已验证,GLX 待 GL 构建)
This commit is contained in:
AskaEth committed 2026-11-07 08:28:36 +08:00
1 parent aaba339499
commit 64fe489a04
4 files changed
+118 -32

No files matched your search

+8 -8
View File
@@ -14,8 +14,8 @@
| 横屏适配 | ✅ | 默认窗口按 X 屏幕等比自适应(`source=termux-screen`) |
| Java 发现 | ✅ | `JAVA_HOME`/PATH + `$PREFIX/lib/jvm` 扫描 |
| Java 自动安装 | ✅ | `ull java install 17` 由内置镜像装完 openjdk-17(约 20 s) |
| GPU(wgpu) | ⛔ | glibc 侧 Mesa 24.2.6 不支持 Adreno 840;Arch 26.2.6 缺 KGSL。CPU 渲染可用 |
| Minecraft 运行 | ⏳ | 无官方 Android natives;计划走 glibc JVM(见下) |
| GPU(wgpu) | ✅ | 自编 Mesa 26.2.6 + KGSL;wgpu 选中 Adreno 840(Vulkan/turnip) |
| Minecraft 运行 | 🟡 | glibc JRE(Temurin 21.0.13)可跑,AWT/X11 正常;GLX 待 Mesa 26 GL 构建 |
## 快速开始
@@ -34,17 +34,17 @@ DISPLAY=:0 ./unlucky-launcher-iced --software-renderer
## 已知边界
- 仅单机(OnePlus 15 / Android 17)验证,试验级、不在发布矩阵内。
- **GPU**:bionic 侧 zink/turnip 可用;glibc 侧被驱动挡住——Termux 的 glibc Mesa 24.2.6
不认识 Adreno 840,Arch 的 26.2.6 是 Linux/DRM 构建、缺 KGSL。需要 Mesa 26+ 的
glibc+KGSL 构建;在那之前用 CPU(tiny-skia)。GPU 变体会自动降级,不会崩。
- **GPU**:已打通,但驱动是自编的(Mesa 26.2.6 + KGSL),装到
`$PREFIX/glibc/{lib,share/vulkan/icd.d}` 后应用会自动启用;Termux 官方源里的
glibc Mesa 太旧(24.2.6 不认识 Adreno 840)。驱动缺失时自动降级 CPU,不会崩。
- **MC**:Android 原生 natives 走不通(LWJGL 没有官方 Android/bionic 产物,且 GLFW 的
窗口模型与 termux-x11 的 X11 不兼容)。可行路线是 glibc JVM(Temurin linux-aarch64 +
glibc Mesa),让 MC 的 Linux natives 在 glibc 进程里正常加载。
glibc Mesa)——JRE 与 AWT/X11 已验证,GLX 还差一份 Mesa 26 的 GL 构建。
- Termux 上三条测试失败是环境差异(`/proc/*/children`、长路径脱敏、mtime flaky),
PC 的 Linux CI 不受影响。
## 后续
1. Mesa 26+ 的 glibc/KGSL 构建(解锁 GPU)
2. MC 运行链:glibc JVM(Temurin linux-aarch64 + glibc Mesa);proot 作为备选
1. Mesa 26 GLX(zink/softpipe)构建 → MC 的 LWJGL/GLFW 打通
2. MC 运行链接线:启动器在 Termux 上改用 glibc JVM(Temurin),bionic java 留给 UI/CLI
3. 剪贴板/IME 在 termux-x11 下的行为验证
+3
View File
@@ -30,6 +30,7 @@ use super::{MIN_SIZE, WINDOW_SIZE};
mod diagnostics;
mod fallback;
mod termux_gpu;
mod termux_window;
use fallback::{on_failure, respawn, OnFailure};
@@ -40,6 +41,8 @@ pub(crate) fn run(options: Options) -> iced::Result {
let boot_started = Instant::now();
// ① 探测(判据取在哪、日志在 `report_boot` 里统一打)
// Termux + glibc 构建:装了我们自编的 turnip ICD 就先生效,auto 也能选到 GPU。
termux_gpu::enable();
let boot = boot_probe(&options);
let decision = boot.decision;
diagnostics::report_boot(&options, &boot.probe, &decision);
@@ -0,0 +1,58 @@
//! Termux 上自动启用随附的 Vulkan ICD(glibc 侧 turnip,`ull_freedreno.json`)。
//!
//! 背景:Termux 软件源里的 glibc Mesa(24.2.6)不认识 Adreno 840,硬件 GPU 需要用
//! `packaging/docs/termux-glibc-build.md` §5 里自编的 Mesa 26(KGSL)。驱动装在
//! `$PREFIX/glibc/share/vulkan/icd.d/ull_freedreno.json` 时,这里在探测之前把它设进
//! `VK_ICD_FILENAMES` / `VK_DRIVER_FILES`,让 `auto` 也能选到 GPU。
//!
//! 只在 **glibc 构建**(`target_os = "linux"`)上生效:Termux 原生(bionic)构建的
//! `target_os = "android"`,glibc 驱动对它没有意义。用户显式设了这两个变量时不覆盖。
use std::path::{Path, PathBuf};
use unlucky_core::platform::paths::ProcessEnv;
use unlucky_core::platform::termux;
/// 随附 ICD 的相对路径(相对 `$PREFIX`)。
const ICD_REL: &str = "glibc/share/vulkan/icd.d/ull_freedreno.json";
/// 在 GPU 探测之前调用:装了就启用,没装就什么都不做。
pub(super) fn enable() {
if !cfg!(target_os = "linux") {
return;
}
if std::env::var_os("VK_ICD_FILENAMES").is_some()
|| std::env::var_os("VK_DRIVER_FILES").is_some()
{
return;
}
let Some(prefix) = termux::prefix(&ProcessEnv) else {
return;
};
let icd = icd_path(&prefix);
if icd.is_file() {
std::env::set_var("VK_ICD_FILENAMES", &icd);
std::env::set_var("VK_DRIVER_FILES", &icd);
}
}
/// `$PREFIX` 下随附 ICD 的路径(纯函数,测试用)。
fn icd_path(prefix: &Path) -> PathBuf {
prefix.join(ICD_REL)
}
#[cfg(test)]
mod tests {
use super::icd_path;
#[test]
fn icd_path_is_under_the_glibc_prefix() {
let path = icd_path(std::path::Path::new("/data/data/com.termux/files/usr"));
assert_eq!(
path,
std::path::PathBuf::from(
"/data/data/com.termux/files/usr/glibc/share/vulkan/icd.d/ull_freedreno.json"
)
);
}
}
+49 -24
View File
@@ -169,37 +169,62 @@ DISPLAY=:0 .gnu/target/release/unlucky-launcher-iced \
- 已实测:`ull java install 17` 从 SAU 镜像装完 `openjdk-17`(含 `openjdk-17-x`),
约 20 秒;`ull java list` 随后列出 `[system]` 的 17。
## 5. GPU(已排查,当前被驱动挡在 glibc 侧)
## 5. GPU(已打通:自编 Mesa 26.2.6 + KGSL)
termux-x11 的 X 客户端**可以**走硬件:本机 bionic 栈实测
`glxinfo -B` → `zink Vulkan 1.4 (Adreno 840 (MESA_TURNIP))`、`Accelerated: yes`,
`vulkaninfo` 中 Adreno 设备带 `VK_KHR_xcb_surface` / `VK_KHR_xlib_surface`。
termux-x11 的 X 客户端可以走硬件;glibc 侧的问题在**驱动**,不在 loader:
glibc 侧已把链路搭好并实测到具体阻塞点:
1. `vulkan-icd-loader-glibc` + ICD JSON 本身正常。
2. Termux 软件源的 glibc Mesa 24.2.6 不认识 Adreno 840
(`chip_id = 44050A31 is unsupported`)。
3. Arch 的 `vulkan-freedreno 26.2.6` 能加载,但它是 Linux/DRM 构建,缺 KGSL
(`/dev/dri` 对 `untrusted_app` 被 SELinux 拒绝)。
1. **loader 正常**:`vulkan-icd-loader-glibc` + 自写 ICD JSON 能加载 ICD。
2. **Termux 的 glibc Mesa 太旧**:`mesa-vulkan-icd-freedreno-glibc` 是 24.2.6(2024),
`vulkaninfo` 报 `chip_id = 44050A31, gpu_id = 6860 is unsupported`
(Adreno 840 不在该版本的支持表里)。
3. **Arch 的新驱动也走不通**:`vulkan-freedreno 26.2.6`(Arch Linux ARM)能加载、
但它是 Linux/DRM 构建,找不到设备——Android 的 GPU 走 KGSL,而
`/dev/dri` 对 `untrusted_app` 被 SELinux 拒绝(`ls /dev/dri` → Permission denied,
设备节点本身是 `crw-rw-rw-`)。
结论:glibc 侧要用硬件 GPU,需要一个 **Mesa 26+ 的 glibc 构建且带 Android/KGSL 后端**;
现有包都缺一半。在那之前用 CPU(tiny-skia)。
GPU 变体仍可构建并验证降级链:
**解法:给 glibc 自编一份带 KGSL 的 Mesa 26.2.6。** 本机实测通过:
```sh
cargo ... build --release -p unlucky-app-iced # 默认 feature,带 wgpu
VK_ICD_FILENAMES="$PREFIX/glibc/share/vulkan/icd.d/freedreno_icd.aarch64.json" \
VK_DRIVER_FILES="$VK_ICD_FILENAMES" \
DISPLAY=:0 .gnu/target/release/unlucky-launcher-iced --renderer=gpu
# 1) 源码 + 依赖
# mesa-26.2.6.tar.xz(archive.mesa3d.org)
# Debian libstdc++-14-dev 的头($G/gnu-support/usr/include/c++/14)
# meson / ninja(pip 装 meson)
# 2) 把源码里 *.py 的 shebang 从 #!/usr/bin/env python3 改成 Termux python3
# (Android 没有 /usr/bin/env)
# 3) meson setup(cc-glibc 包装器:-c 不带链接参数、C++ 加 -stdlib=libstdc++、
# 共享库链接加 -lstdc++;详见 §3)
meson setup build3 --native-file="$G/mesa-native.ini" --wrap-mode=nofallback \
-Dbuildtype=release -Dstrip=true -Dplatforms=x11 \
-Dvulkan-drivers=freedreno -Dfreedreno-kmds=kgsl \
-Dgallium-drivers=zink,softpipe -Dglx=dri -Dopengl=true -Degl=enabled \
-Dgles1=disabled -Dgles2=enabled -Dllvm=disabled -Dgbm=disabled \
-Dvideo-codecs= -Dzstd=enabled -Dbuild-tests=false
ninja -C build3
```
没有可用驱动时它会打印具名错误并**自动降级**到 tiny-skia(同一进程不能重建事件循环,
降级走 respawn),所以 GPU 变体在只有 CPU 的环境里也能跑。
`--wrap-mode=nofallback` 是为了跳过 libarchive 子项目(它只被 freedreno 的 decode 工具需要,
且它自带的 `id_t` 兜底宏会和 glibc 头冲突)。
安装与启用:
```sh
cp build3/src/freedreno/vulkan/libvulkan_freedreno.so \
"$PREFIX/glibc/lib/libvulkan_freedreno_ull.so"
cat > "$PREFIX/glibc/share/vulkan/icd.d/ull_freedreno.json" <<EOF
{"ICD":{"api_version":"1.4.335","library_path":"$PREFIX/glibc/lib/libvulkan_freedreno_ull.so"},"file_format_version":"1.0.0"}
EOF
```
应用启动时(glibc 构建 + Termux + 该 JSON 存在)会自动把它设进
`VK_ICD_FILENAMES` / `VK_DRIVER_FILES`(`app/boot/termux_gpu.rs`);用户显式设了这两个
变量时不覆盖。实测日志:
```text
[probe] adapters=1 [Adreno (TM) 840 [IntegratedGpu]] hardware=true :: request=auto -> ICED_BACKEND=wgpu
[gpu] Selected: AdapterInfo { name: "Adreno (TM) 840", driver: "turnip Mesa driver",
driver_info: "Mesa 26.2.6", backend: Vulkan }
[shot] 1657x1067 renderer=wgpu,首帧 61 ms
```
没装驱动/没有 GPU 时仍会打印具名错误并自动降级 tiny-skia(同一进程不能重建事件循环,
降级走 respawn),所以 GPU 变体在纯 CPU 环境也能跑。
## 6. 已知边界