Skip to content

feat: 添加 SDL3 以支持启动 26.3 snapshot 4+ & 支持 Controlify 模组 - #1721

Merged
MovTery merged 26 commits into
mainfrom
feat/sdl3
Aug 28, 2026
Merged

feat: 添加 SDL3 以支持启动 26.3 snapshot 4+ & 支持 Controlify 模组#1721
MovTery merged 26 commits into
mainfrom
feat/sdl3

Conversation

@MovTery

@MovTery MovTery commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

大部分实现参考 Amethyst: https://github.com/AngelAuraMC/Amethyst-Android

  • 解决了 OpenGL 后端始终创建失败的问题
  • 解决了 OpenGL 后端无法捕获帧率的问题
  • 解决了无法启动 MC 26.3 snapshot 9+ 的问题
  • 解决了无法使用 Turnip 驱动加载 Vulkan 后端的问题(SDL 加载 Vulkan)
  • 加入 “自动唤起输入法(SDL)” 设置项
  • 加入 “手柄输入模式” 设置项(映射(虚拟按键)SDL 直通

推荐使用 MobileGL 渲染器运行!

Close: #1693 #1668 #1655 #1619 #1618 #1616 #378

MovTery added 26 commits August 22, 2026 05:21
- Direct gamepad 桥补全(对齐 AAMC)
  - nativeEnableGamepadDirectInput 由空桩改为转发 launcher 侧 onDirectInputEnable
  - LWJGL enableGamepadDirectInput 一次性包装;app 侧创建共享 GLFWgamepadstate 缓冲
  - 新增 DirectGamepad/DirectGamepadEnableHandler:手柄事件标准化写入缓冲,游戏经 glfwGetGamepadState 读取
- 手柄输入模式设置
  - 新增 GamepadInputMode(映射虚拟按键 / SDL 直通),设置页置顶 + 游戏菜单手柄分页可切换
  - SDL 直通时禁用全部重映射设置;中英繁文案
- SDL 直通输入通路修复
  - 直通模式消费手柄事件,避免被系统 onBackPressed 吞键
  - SDL 未启用时丢弃事件而非回落映射,防止触发 remapper 绑定与 UnsatisfiedLinkError
  - 按钮改走 SDLActivity.handleKeyEvent(onNativePadDown/Up 带 deviceId/scancode),修复手柄按钮在 SDL 3 下无响应
- SdlBridge.initializeControllerSubsystems:launcher VM 内强制初始化 SDL GAMEPAD/JOYSTICK/EVENTS
- sdl_dlopen_hook 对齐 AAMC 安全实现:不再 hook dlopen(Turnip loader 冲突),dlsym 兜底获取 SDL handle
- 移除调试用手柄事件链日志
- LWJGL 3.3.3/3.4.1 glfwGetWindowContentScale 由硬编码 1f 改为
  CallbackBridge.nativeGetAndroidDPI()(对齐 AAMC 实现)
- app CallbackBridge.getAndroidDPI() 返回 density × resolutionRatio/100:
  窗口坐标空间随启动器分辨率缩放(getDisplayFriendlyRes),content scale
  需同步(与 AAMC 的 density × PREF_SCALE_FACTOR 同构)
- LWJGL 3.3.3 CallbackBridge 补充 nativeGetAndroidDPI 声明
移动渲染器(MobileGlues 等)均为 OpenGL ES 实现,而游戏按桌面 GL 惯例
初始化 SDL,导致 26.3 快照版的 OpenGL 后端在部分设备上创建失败:

- 在窗口创建入口(SDL_CreateWindow / SDL_CreateWindowWithProperties)
  强制 GL profile 为 ES
- 接管 SDL_LoadFunction 对 eglChooseConfig / eglCreateContext 的解析,
  注入归一化代理,使所有调用路径生效:
  - eglChooseConfig:RENDERABLE_TYPE 携带的 ES3_BIT / OPENGL_BIT 归一化
    为 ES2_BIT(部分宿主 libEGL 不接受),实际上下文版本仍由
    CLIENT_VERSION 决定
  - eglCreateContext:剔除 EGL_CONTEXT_MAJOR/MINOR_VERSION_KHR(部分
    宿主不识别、直接报 EGL_BAD_ATTRIBUTE),仅保留 CLIENT_VERSION;
    创建失败时降级为 CLIENT_VERSION=2 重试并输出警告日志
- 修正 SDL_InitSubSystem 错误日志把 tag 当格式串传入的问题
MC 26.3-snapshot-8 起在 RenderSystem.initBackendSystem 中于 SDL_Init
之前设置 SDL_ENABLE_SCREEN_KEYBOARD=0(按桌面惯例改用自绘 IME UI),
导致移动端无法唤起软键盘,且手动唤起后注入的文本因文本输入链路被
压制而无法到达游戏。

在 SDL_InitSubSystem hook 中(此时 MC 设置的 hint 已生效、原始
SDL_Init 尚未执行)将该 hint 覆盖为 1,恢复移动端依赖的屏幕键盘。
MC 26.3 起 RenderPearl 要求 SDL 与 LWJGL 使用同一 Vulkan 加载器实例
(校验 vkGetInstanceProcAddr 指针一致)。启动器经 EGLBridge 将 LWJGL
的句柄重定向到私有命名空间中的加载器副本(Turnip 链路,句柄记录于
VULKAN_PTR 环境变量);而 SDL 仅能按路径加载、无法触及该私有实例,
回落到系统 libvulkan.so 后指针比对必然失败,报
vkGetInstanceProcAddr mismatch。

- SDL_LoadObject:请求加载 libvulkan* 时改还 VULKAN_PTR 句柄,
  使 SDL 的函数解析与 surface 创建全链和 LWJGL 同源
- SDL_UnloadObject:忽略对该句柄的卸载(引用计数由启动器持有,
  避免 RenderPearl 的 load/unload 探测流程把模块引用减到 0)
libSDL3 将线程 JNIEnv 缓存于自身 TLS(mJavaVM 经 sdl_dlopen_hook 绑定
ART)。calculateFPS/nativeClipboard/nativeSetCursorShape 原先无条件
DetachCurrentThread,会使渲染线程在 SDL 中缓存的 env 悬空,后续经 SDL
的 JNI 调用(如光标切换 SDL_SetCursor → setSystemCursor)解引用失效
JNIEnv 导致 pc=0 的 SIGSEGV。改为仅当本次调用新建附着时才 Detach。
libSDL3 的 Android_JNI_ShowMessageBox 会调用 SDLActivity.getContext(),
并在返回对象的运行时类上查找 messageboxShowMessageBox;本启动器宿主是
VMActivity(非 SDLActivity 子类),查找失败后带着 pending 异常继续
CallIntMethod,触发 ART JniAbort SIGSEGV。

- 在 SDLActivity 新增带宿主参数的静态 messageboxShowMessageBox:
  MaterialAlertDialogBuilder 构建(MD3),阻塞游戏线程直至用户点击;
  原实例方法改为一行委托,移除旧的 messageboxCreateAndShow 实现
- VMActivity 以 @keep 标注同名同签名方法桥接到该静态版本,
  保证 R8 下原生层按名查找可用
- 该文件 javadoc 暂不使用内联标签:JDK 17 上 lint 的 K2 分析器解析
  内联标签会因 NoSuchMethodError: List.removeLast() 使
  lintVitalAnalyzeRelease 失败,构建 JDK 升级至 21+ 后可恢复
排查 RenderPearl 后处理阶段 Adreno 驱动 SIGSEGV 过程中,对 sdl_hook
的窗口与 EGL 介入点做健壮性与语义修正:

- EGL config/context 兼容改为原始请求优先,仅在宿主拒绝后降级重试;
  修正仅含 KHR 版本属性时 CLIENT_VERSION 被丢弃的问题(ES 语义下补写,
  desktop 语义下不补写)
- GLES 兼容层增加渲染器门控:排除 desktopgl(Kopper 桌面 GL) 与
  gallium_*/vulkan_zink 等 OSMesa 路径,避免桌面请求被强制 ES 化;
  MobileGlues 判定兼容绝对路径形式的 POJAVEXEC_EGL
- 补齐 SDL_EGL_GetProcAddress 公共解析入口的代理注入(可绕过
  SDL_LoadFunction);原始 EGL 函数指针首次解析后固定,避免多次解析
  不同 handle 时跨 loader 调用;swap 代理补充空函数指针保护
- 主窗口复用增加逻辑引用计数:MC 的隐藏工具窗口与主窗口共用 Android
  单一真实 SDL 窗口,销毁延迟至最后一个逻辑引用释放;新增
  POJAV_SDL_REUSE_WINDOW 诊断开关(=1 强制启用,其余值禁用)
- 销毁窗口时同步清理事件窗口缓存指针,防止悬空 SDL_Window* 回落

设备实测:OpenGL + MobileGlues 正常启动并完成 GUI 切换压力测试。
根因:原实现在 showTextInput 入口直接 return false,跳过了整个
ShowTextInputTask。该任务除弹出软键盘外还负责创建/聚焦隐藏的
SDLDummyEdit——Android 软键盘文字只有经它的 InputConnection 才能合成
SDL_TEXT_INPUT 事件。任务被整体拒绝后,SDL 侧 text input 已激活、
MC 在等待文本事件,而 Android 侧没有任何可编辑视图接收提交,导致
即使手动唤起 IME 输入也无响应。

修复:将设置判断下沉到任务内部,仅跳过 imm.showSoftInput 自动弹出,
保留 DummyEdit 的创建、布局与焦点准备;关闭选项时用户手动唤起的
IME 仍附着到已持有焦点的 DummyEdit 上,输入照常生效。
@MovTery
MovTery merged commit b265771 into main Aug 28, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TECNO Spark Go 2024 Crash not working jvm -3 26.3 snapshot 8/9

1 participant