OpenGL 3.3 Core Profile → Vulkan 1.2 (via MoltenVK) → Metal 翻译层,让依赖桌面 OpenGL 的应用能在仅有 Metal 后端的 iOS 上运行。
Mithril-Wrapper 把宿主程序发出的 OpenGL 3.3 Core Profile 调用实时翻译成
Vulkan 1.2 调用,再由 MoltenVK(静态链接)将 Vulkan 调用交叉翻译为
Metal 调用。库自带一套基于 Vulkan + CAMetalLayer 的 EGL 1.5 实现,
让 LWJGL 3 / PojavLauncher / Amethyst-iOS 这类靠 eglCreateContext 等 EGL 入口
拉起 GL 上下文的启动器可以直接 dlopen 本库。着色器走
GLSL → SPIR-V 即时转译管线 —— MoltenVK 在 vkCreateShaderModule 时内部把
SPIR-V 交叉翻译成 MSL:
GLSL 源码 ──glslang──▶ SPIR-V ──vkCreateShaderModule──▶ [MoltenVK SPIR-V→MSL] ──▶ MTLLibrary
项目结构参考了 MobileGlues 的
MobileGlues-cpp/ 布局,但目标 API 不同:MobileGlues 做的是
桌面 GLSL → GLSL ES ;Mithril-Wrapper 直接落到 Vulkan 1.2,
通过 MoltenVK 静态链接到 Metal,且自带 EGL,不再依赖 ANGLE 的
libEGL.framework。
- 对外暴露一整套
extern "C"的 OpenGL 3.3 Core 入口(glDraw*、glBindBuffer、glTexImage2D、glUniform*、glGetString*等), 可作为动态库libmithril.dylib被dlopen注入。 glGetString(GL_VERSION)返回3.3 §bMithril-Wrapper§r 1.0 (Vulkan 1.2 / MoltenVK),glGetIntegerv(GL_MAJOR_VERSION/GL_MINOR_VERSION)返回3 / 3,GL_CONTEXT_PROFILE_MASK返回GL_CONTEXT_CORE_PROFILE_BIT。- 自带 EGL 1.5(Vulkan 后端):
egl/egl.cpp导出 ~44 个egl*入口 (EGL 1.5 全套:eglGetDisplay/eglInitialize/eglChooseConfig/eglCreateContext/eglCreateWindowSurface/eglMakeCurrent/eglSwapBuffers+ EGL 1.5 Sync / Image / Platform Surface API …),宿主启动器 (如 Amethyst-iOS 的Natives/ctxbridges/gl_bridge.m)可直接dlsym解析。 EGLDisplay 映射到单例 Vulkan 实例/设备;EGLSurface 包装原生窗口 (CAMetalLayer)+mithril::vk::Swapchain*, 每帧由vkAcquireNextImageKHR拉取的VkImageView直接挂到 GL 状态机的默认 帧缓冲(FBO 0)上,GL 绘制命令因此直接渲染到屏幕 drawable;EGLContext 各自 持有独立的mithril::GLState,eglMakeCurrent切换mithril::g_state指向 当前上下文。新增的 EGL 1.5 Sync/Image API 为影子实现(参考 MobileGL,仅 维护状态层,不创建真实VkFence/VkImage);平台相关 surface 创建由SurfaceMetal.mm承担,覆盖 Apple (Metal) 平台。 - Vulkan 后端(
MG_Backend/DirectVulkan/):Device——VkInstance/VkPhysicalDevice/VkDevice/VkQueue/VkCommandPool生命周期与端口性枚举(VK_KHR_portability_enumeration+VK_KHR_portability_subset+VK_KHR_swapchain+VK_KHR_dynamic_rendering)。Resources——VkBuffer/VkImage/VkImageView/VkSampler按 GL 名字托管 + 暂存上传路径 + GL internalFormat → VkFormat 映射。Pipeline—— 从 SPIR-V 构建VkShaderModule,并按(程序, 顶点格式, 附件格式, 混合状态, 图元模式)的哈希签名缓存VkGraphicsPipeline。Swapchain—— 拆分为SwapchainCommon.cpp(共享后处理:格式查询 /vkCreateSwapchainKHR/ image views / 深度模板)+SwapchainMetal.mm(Apple,VK_EXT_metal_surface把CAMetalLayer包成VkSurfaceKHR)。 深度/模板格式VK_FORMAT_D32_SFLOAT_S8_UINT。SwapchainMetal.mm编译为 Objective-C++(.mm),因为需要VK_USE_PLATFORM_METAL_EXT宏定义。CommandStream—— Vulkan dynamic rendering (VK_KHR_dynamic_rendering) 的渲染通道编排与命令缓冲区管理。DescriptorSet—— 通过 SPIRV-Cross 反射 VS+FS 的 SPIR-V,发现 UBO 和 sampled image 绑定,构建VkDescriptorSetLayout/VkPipelineLayout/VkDescriptorPool。FormatMap—— GL internalFormat 到 VkFormat 的映射表。ImageOps—— 纹理的 blit、mipmap 生成、readPixels 等图像操作。Reflect—— SPIR-V 反射辅助工具,封装 SPIRV-Cross 的常用反射查询。
- 着色器转译(
MG_Impl/Shader.cpp):线程安全地调用 glslang 把 GLSL 3.30 编译成 Vulkan SPIR-V(EShClientVulkan+EShTargetVulkan_1_2+EShTargetSpv_1_5),并在预处理阶段注入MG_MITHRIL/MG_MITHRIL_VERSION宏以及glBindAttribLocation映射的layout(location=N)。自动将 GLSL 版本升级到 330+。MoltenVK 在vkCreateShaderModule内部把 SPIR-V 交叉翻译成 MSL。
| 项 | 要求 | 说明 |
|---|---|---|
| SoC | Apple A11 及以上 | iPhone 8 / 8 Plus / X 起步;A11 是首个支持 Metal 的芯片,也是 MoltenVK 1.2 portability subset 的最低起步 |
| 系统 | iOS / iPadOS 15.0 及以上 | CI 默认部署目标 15.0(MoltenVK v1.4.x 已提升最低版本) |
| 架构 | arm64 | CI 仅构建 PLATFORM=OS64;不支持 armv7/armv7s |
| Vulkan | Vulkan 1.2 (MoltenVK 静态链接) | CI 从 KhronosGroup/MoltenVK release 拉取 MoltenVK.xcframework,CMake 用 find_library 解析 |
| MSL | 目标 MSL 2.3 | iOS 15 对应的 Metal Shading Language 版本;MoltenVK 自动选择 |
| 宿主 | 任意支持 dlopen 注入渲染器的启动器 |
已验证可对接 Amethyst-iOS(ui/fcl-versionmgr 系) |
Vulkan 后端只使用 Vulkan 1.2 核心 + VK_KHR_dynamic_rendering +
portability subset 所需的最小扩展集,不依赖 Vulkan 1.3 的同步、动态渲染
核心提升等可选特性,因此 A11 / iOS 15 设备上可完整运行 Minecraft Java
Edition 的现代渲染管线。
Warning
低于 A11 的设备(A7 / A8 / A8X / A9 / A10)不受支持:A7–A8 仅支持
Metal 1.x,MoltenVK 1.2 portability subset 在 A9–A10 上虽可启动但缺少
本实现依赖的若干 VK_FORMAT_D32_SFLOAT_S8_UINT 性能优化路径。最低起步即
iPhone 8 / iPhone X(A11, Metal, iOS 15)。
GL 调用的具体实现层。每个 gl* 函数通过 MG_Backend/Backend.h 定义的 C API
调用 Vulkan 后端。主要文件:
gl.cpp—— 核心 GL 状态切换入口(glBind*、glEnable/Disable等)Buffer.cpp——glGenBuffers、glBindBuffer、glBufferData等Texture.cpp——glGenTextures、glTexImage2D、glTexParameter等Drawing.cpp——glDrawArrays、glDrawElements、glClear等Program.cpp——glCreateProgram、glLinkProgram、glUseProgram等Shader.cpp——glCreateShader、glShaderSource、glCompileShader(含 GLSL→SPIR-V 编译)Framebuffer.cpp——glGenFramebuffers、glFramebufferTexture2D等VertexArray.cpp——glGenVertexArrays、glVertexAttribPointer等Getter.cpp/Getter_gpu.mm——glGetString、glGetIntegerv等查询EGLConfig.cpp—— EGL 配置匹配表(纯逻辑,可单元测试)init.cpp—— 全局初始化与清理Stubs.cpp—— 未实现的 GL 入口桩lookup.cpp——glXGetProcAddress入口查找Log.cpp/Log.h—— 日志基础设施Debug.cpp—— 调试辅助
GLState 结构体持有所有 GL 状态、对象表(buffer、texture、shader、program、
framebuffer、VAO),以及 EGL 默认帧缓冲的 VkImageView。每个 EGLContext
拥有独立的 GLState,eglMakeCurrent 切换 mithril::g_state 全局指针。
Device.cpp/h——VkInstance/VkDevice/VkQueue/VkCommandPool生命周期Resources.cpp/h——VkBuffer/VkImage/VkImageView/VkSampler管理Pipeline.cpp/h——VkShaderModule构建 +VkPipeline哈希缓存CommandStream.cpp/h——VK_KHR_dynamic_rendering动态渲染通道编排DescriptorSet.cpp/h—— SPIRV-Cross 反射 UBO/sampler 绑定,构建描述符布局Swapchain.h/SwapchainCommon.cpp—— 共享 swapchain 逻辑(格式查询 /vkCreateSwapchainKHR/ image views / 深度模板)SwapchainMetal.mm—— Apple 路径(VK_EXT_metal_surface,Objective-C++)FormatMap.cpp/h—— GL internalFormat → VkFormat 映射ImageOps.cpp—— blit、mipmap 生成、readPixels 等图像操作Reflect.cpp/h—— SPIR-V 反射辅助(封装 SPIRV-Cross)
egl.cpp—— 跨平台 EGL 1.5 核心(~44 个egl*入口 + 影子 sync/image 对象)。 无平台相关 include;surface 创建通过surface_create()/surface_get_size()分派到平台文件。EGLDisplay → 单例 Vulkan 实例/设备;EGLSurface → 原生窗口 +VkSwapchainKHR;EGLContext → 独立GLState。SurfaceMetal.mm—— AppleCAMetalLayer强制转换(CALayer→CAMetalLayer)- drawable 尺寸查询(Objective-C++)
GLState 结构体(MG_State/State.h)包含:
- 所有 GL 开关状态(blend、depth test、cull face、stencil 等)
- 对象表:
std::unordered_map<GLuint, ...>管理 buffers、textures、shaders、 programs、framebuffers、VAOs、samplers - EGL 默认帧缓冲
VkImageView(color + depth/stencil) - 每上下文隔离,
eglMakeCurrent切换全局g_state指针
Shader.cpp 实现 GLSL → SPIR-V 编译管线:
- 预处理阶段注入
MG_MITHRIL/MG_MITHRIL_VERSION宏 - 自动升级 GLSL 版本到 330+(Vulkan GLSL 最低要求)
- 应用
glBindAttribLocation映射,注入layout(location=N) - 调用 glslang 编译(
EShClientVulkan+EShTargetVulkan_1_2+EShTargetSpv_1_5) - 线程安全(
std::mutex保护),带缓存
MoltenVK 在 vkCreateShaderModule 时自动将 SPIR-V 交叉翻译为 MSL。
SPIRV-Cross 仅用于 SPIR-V 反射(非翻译)。DescriptorSet.cpp 在程序链接后
反射 VS+FS 的 SPIR-V,发现 UBO 和 sampled image 的 binding 信息,进而构建
VkDescriptorSetLayout / VkPipelineLayout / VkDescriptorPool。
实际的 SPIR-V → MSL 翻译仍由 MoltenVK 在 vkCreateShaderModule 时内部完成。
CommandStream.cpp 使用 VK_KHR_dynamic_rendering 扩展替代传统
VkRenderPass / VkFramebuffer 对象,简化渲染通道管理。
Pipeline.cpp 按 (program, 顶点格式, 附件 VkFormat 列表, 混合状态, 图元模式)
的哈希签名缓存 VkGraphicsPipeline,避免重复创建。
egl/egl.cpp 提供完整 EGL 1.5 实现(~44 个 egl* 入口,EGL 1.5 全套):
EGLDisplay→ 单例 Vulkan 实例/设备EGLSurface→ 原生窗口 +VkSwapchainKHR(平台对应:AppleVK_EXT_metal_surface)EGLContext→ 独立GLState,eglMakeCurrent切换上下文eglSwapBuffers→ 提交命令 +vkAcquireNextImageKHR+ 呈现- EGL 1.5 Sync API(
eglCreateSync/eglClientWaitSync/ …)—— 影子实现, 参考 MobileGL:仅维护状态层(始终 signaled),不创建真实VkFence - EGL 1.5 Image API(
eglCreateImage/eglDestroyImage)—— 影子实现, 不创建真实VkImage,仅返回进程内句柄
项目不直接使用 Metal 框架——所有 Metal 交互通过 MoltenVK 内部完成。
CAMetalLayer 仅用于 VK_EXT_metal_surface 创建 Vulkan surface。
| 平台 | Vulkan surface 扩展 | 原生窗口 | 备注 |
|---|---|---|---|
| iOS / macOS | VK_EXT_metal_surface + CAMetalLayer |
CAMetalLayer* |
主路径,CI 实际构建 |
egl/Surface<Platform>.cpp/mm 提供 surface_create() / surface_get_size(),
由 CMake 按 APPLE 守卫选择。
.
├── CMakeLists.txt # 顶层构建脚本(add_subdirectory glslang/SPIRV-Cross + find MoltenVK.xcframework)
├── .gitmodules # 三个子模块:glslang、SPIRV-Cross、SPIRV-Headers
├── .github/workflows/
│ └── build.yml # CI:macOS arm64 交叉编译 iOS dylib
├── Mithril-Wrapper-cpp/ # 源码根(参考 MobileGlues 的布局)
│ ├── MG_Impl/ # OpenGL 3.3 Core Profile 实现(Vulkan 后端)
│ │ ├── includes.h # 全局内部头
│ │ ├── init.cpp gl.cpp Getter.cpp Program.cpp Shader.cpp
│ │ ├── Buffer.cpp Texture.cpp Framebuffer.cpp Drawing.cpp
│ │ ├── VertexArray.cpp Stubs.cpp Debug.cpp lookup.cpp
│ │ ├── EGLConfig.{h,cpp} # EGL 配置匹配表
│ │ ├── Getter_gpu.mm # GPU 名称字符串构建(读 VkPhysicalDeviceProperties)
│ │ ├── Log.{h,cpp} Shader.{h,cpp} Framebuffer.h
│ ├── MG_State/ # GL 状态机
│ │ └── State.{h,cpp}
│ ├── MG_Backend/ # 抽象后端 C API(Backend.h)
│ │ ├── Backend.h
│ │ └── DirectVulkan/ # Vulkan 1.2 + MoltenVK 直接后端
│ │ ├── Device.{h,cpp} # VkInstance/Device/Queue/CommandPool
│ │ ├── Resources.{h,cpp} # VkBuffer/Image/ImageView/Sampler
│ │ ├── Pipeline.{h,cpp} # VkShaderModule + VkPipeline 缓存
│ │ ├── CommandStream.{h,cpp} # 动态渲染通道编排(VK_KHR_dynamic_rendering)
│ │ ├── DescriptorSet.{h,cpp} # SPIRV-Cross 反射描述符布局
│ │ ├── FormatMap.{h,cpp} # GL → VkFormat 映射
│ │ ├── ImageOps.cpp # 图像操作(blit/mipmap/readPixels)
│ │ ├── Reflect.{h,cpp} # SPIR-V 反射辅助
│ │ ├── Swapchain.h # Swapchain 接口(平台无关)
│ │ ├── SwapchainCommon.cpp # 共享 swapchain 逻辑(格式查询 / vkCreateSwapchainKHR / image views / 深度模板)
│ │ └── SwapchainMetal.mm # Apple 路径(VK_EXT_metal_surface,.mm)
│ ├── egl/ # EGL 1.5(跨平台核心 + 平台 surface 分裂)
│ │ ├── egl.cpp # ~44 个 egl* 入口 + 影子 sync/image 对象(跨平台 C++)
│ │ └── SurfaceMetal.mm # Apple CAMetalLayer 强制转换 + 尺寸查询(.mm)
│ ├── include/ # 对外公共头
│ │ ├── GL/ # gl.h、glcorearb.h
│ │ ├── KHR/ # khrplatform.h
│ │ └── EGL/ # egl.h(自带 EGL 类型 + PFNEGL*PROC typedef)
│ └── 3rdparty/ # Git 子模块
│ ├── glslang # GLSL → SPIR-V 前端
│ ├── SPIRV-Cross # SPIR-V 反射(仅用于反射,非翻译)
│ └── SPIRV-Headers # SPIR-V 头文件(spv:: 枚举)
- CMake ≥ 3.22
- C++20 编译器(clang / Apple clang)
- MoltenVK.xcframework(静态,Vulkan 1.2 over Metal)— CI 自动下载
v1.4.2,本地构建请从 KhronosGroup/MoltenVK releases
下载
MoltenVK-ios.tar解压后放到仓库根目录的MoltenVK.xcframework+MoltenVK-Headers,或通过-DMOLTENVK_ROOT=/path/to/extracted指定。 - Metal 框架——项目不直接使用 Metal API,仅通过 MoltenVK 内部调用。
CAMetalLayer 用于
VK_EXT_metal_surface创建 Vulkan surface。 - Git 子模块(glslang、SPIRV-Cross、SPIRV-Headers),见
.gitmodules - SPIRV-Cross —— 运行时用于 SPIR-V 反射(
DescriptorSet.cpp通过spirv_cross::Compiler+get_shader_resources发现 UBO/sampler 绑定) - SPIRV-Headers —— 提供
<spirv.h>(spv::Decoration*等枚举)
git clone --recursive https://github.com/EternityQwQ/Mithril-Wrapper.git
cd Mithril-Wrapper
# 如果已经克隆但忘了带 --recursive:
git submodule update --init --recursive# 选一个 tagged release(示例用 v1.4.2)
MOLTENVK_TAG="v1.4.2"
curl -fsSL -o MoltenVK-ios.tar \
"https://github.com/KhronosGroup/MoltenVK/releases/download/${MOLTENVK_TAG}/MoltenVK-ios.tar"
tar -xf MoltenVK-ios.tar
# 解压后找到 static MoltenVK.xcframework 和 MoltenVK-Headers 并移到仓库根目录
mv MoltenVK/MoltenVK/static/MoltenVK.xcframework ./MoltenVK.xcframework
mv MoltenVK/MoltenVK/include ./MoltenVK-Headerscmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
# 产物:build/libmithril.dylibCI 使用的就是这条路径。借助 leetal/ios-cmake 工具链:
# 下载工具链
curl -fsSL -o ios.toolchain.cmake \
https://raw.githubusercontent.com/leetal/ios-cmake/master/ios.toolchain.cmake
# 配置(iOS arm64,部署目标 15.0)
cmake -S . -B build-ios \
-DCMAKE_TOOLCHAIN_FILE=ios.toolchain.cmake \
-DPLATFORM=OS64 \
-DDEPLOYMENT_TARGET=15.0 \
-DCMAKE_BUILD_TYPE=Release
cmake --build build-ios -j
# 产物:build-ios/libmithril.dylib (arm64 iOS)构建产物是单个共享库(libmithril.dylib),可注入到目标进程的 OpenGL / EGL
加载路径中。该库同时导出 gl*(OpenGL 3.3 Core)与 egl*(EGL 1.5,~44 个入口)
符号,宿主启动器只需 dlopen("@rpath/libmithril.dylib", RTLD_NOW) 即可同时拿到
两套入口。MoltenVK 静态链接进 dylib(仅 Apple 构建),所以不需要在目标设备上
额外安装 Vulkan ICD 或 VK_ICD_FILENAMES。
本仓库的 EGL 实现专门用于对接
Amethyst-iOS 的 Mithril 渲染器
(eglCreateContext dlsym 路径)。集成步骤:
- 把 CI 产物
libmithril.dylib放到 Amethyst 应用的Frameworks/目录。 - 在 Amethyst 的渲染器选项里选择
Mithril(即RENDERER_NAME_MTL_ANGLE对应的入口),让gl_bridge.m把libmithril.dylib当作 EGL 宿主加载。 egl_bridge.m/gl_bridge.m调用eglGetDisplay(EGL_DEFAULT_DISPLAY)→eglInitialize→eglChooseConfig→eglCreateWindowSurface(layer)→eglCreateContext→eglMakeCurrent,全部由本 dylib 解析并落到 Vulkan 1.2 → MoltenVK → Metal。
对应分支:Amethyst-IOS
(基于 herbrine8403/Amethyst-iOS-MyRemastered@ui/fcl-versionmgr)。
GitHub Actions 工作流 .github/workflows/build.yml
会在 macos-latest runner 上:
- 检出仓库(带子模块)。
- 下载
ios.toolchain.cmake工具链。 - 下载
MoltenVK.xcframework(v1.4.2 静态)到仓库根目录。 - 用 ios-cmake 工具链交叉编译 iOS arm64 的
libmithril.dylib(部署目标 15.0)。 - 用
nm -gU校验所有egl*入口都进入导出表。 - 上传
libmithril.dylib为 artifact。
每次推送到 main 都会触发。
- MobileGlues —— 目录结构与 GL 状态管理思路的参考。
- KhronosGroup/MoltenVK —— Vulkan 1.2 over Metal 实现,本项目静态链接其 .xcframework。
- KhronosGroup/glslang —— GLSL 参考编译器前端,用于 GLSL → SPIR-V 即时翻译。
- KhronosGroup/SPIRV-Cross —— SPIR-V 反射库,用于描述符布局构建。
- KhronosGroup/SPIRV-Headers —— SPIR-V 头文件。
- KhronosGroup/Vulkan-Headers —— Vulkan 头文件(随 MoltenVK.xcframework 一起分发)。
- leetal/ios-cmake —— iOS CMake 工具链。
- EternityQwQ
- yitenchen123
- Uniaball
详见 LICENSE。