ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

如何把 x86 游戏画面显示到 iPhone 上:Madeira 的 IOSDisplayShim + CAMetalLayer 显示桥接详解

如何把 x86 游戏画面显示到 iPhone 上:Madeira 的 IOSDisplayShim + CAMetalLayer 显示桥接详解 如何把 x86 游戏画面显示到 iPhone 上Madeira 的 IOSDisplayShim CAMetalLayer 显示桥接详解【免费下载链接】MadeiraRun x86-64 Windows PC games on jailed iOS via FEX-Emu Wine DXMT项目地址: https://gitcode.com/GitHub_Trending/mad/MadeiraMadeira 是一个让未越狱 iPhone 直接运行 x86-64 Windows PC 游戏的开源项目它把 FEX-Emux86→ARM64 翻译、WineWindows 兼容层和 DXMTD3D11→Metal 翻译打包成单个 iOS 进程运行。而这一切能否真正看见关键就在显示链路——本文带你读懂 IOSDisplayShim.m 如何用UIKit CAMetalLayer实现 Wine 显示驱动把 Wine 客端的每一帧画到 iPhone 屏幕上。一、背景为什么 iOS 上的 Wine 需要一个显示垫片层DXMT 负责把 Windows 的 D3D11 调用翻译成 Apple 的 Metal 指令但它最初是为macOS设计的它的 unix 端代码会假设一个类似 Cocoa 的显示驱动存在通过下面这条链拿到渲染目标dlsym(RTLD_DEFAULT, macdrv_functions) → get_win_data(hwnd) → client_cocoa_view → macdrv_view_create_metal_view → macdrv_view_get_metal_layer 拿到 CAMetalLayer开画问题在于iOS 上根本不存在 Cocoa也没有窗口列表这个概念——整个设备只有一块屏幕。IOSDisplayShim 的思路非常巧妙既然 DXMT 只认符号名那我就在主程序里伪造出一套它期望的 macdrv 函数表并把所有 HWND 都指向同一块 Swift 端创建的 CAMetalLayer。这正是项目 README 所说的核心机制详见 README.md 与 IOSDisplayShim.h 头文件注释。二、核心机制用 200 行代码骗过 DXMT整个垫片层只有约 260 行IOSDisplayShim.m核心分四步。1. 进程级单例一块层服务全局iOS 只有一个窗口所以全局只保留一块层指针用 pthread 互斥锁保护static CAMetalLayer *g_layer nil; // 全局唯一渲染层 void madeira_display_set_layer(CAMetalLayer *layer) { pthread_mutex_lock(g_lock); g_layer layer; pthread_mutex_unlock(g_lock); }见 IOSDisplayShim.m。Swift 前端在游戏启动、D3D11 交换链创建之前调用这个函数把自建的层注册进来。2. 伪造窗口数据把 HWND 装进结构体DXMT 每次渲染都要先问这个窗口HWND对应的视图是什么。垫片层用一个静态假结构体把 HWND 塞进client_cocoa_view字段带着走my_get_win_data()记住传入的 HWND返回伪造结构体指针my_view_create_metal_view()这是最关键的两个函数之一根据运行模式返回真正的层运行模式行为游戏模式默认返回全局g_layer即全屏单例层桌面模式MADEIRA_DESKTOP1调用 Winios.m 的桌面合成器按窗口取每个窗口各自独立的 CAMetalLayer返回前用CFBridgingRetain给层加一次引用计数macdrv_view_release_metal_view时再释放——Objective-C 的 ARC 记账就这样被正确接进了 DXMT 的创建/释放生命周期里。3. 取出层只是原样返还macdrv_view_get_metal_layer直接返回上一步打包的指针因为my_view_create_metal_view返回的就是层本身这一步只是把视图句柄和层句柄划等号。见 IOSDisplayShim.m。4. 符号导出一行属性决定生死 ⚠️这是全文最硬核的部分。DXMT 靠dlsym按名字找符号链接器根本看不见这种引用。Release 模式下-dead_strip会把这些无人引用的函数整个删掉DXMT 就查不到驱动、直接报your Wine has no exported symbols needed by DXMT白屏退出。项目的解法是在每个导出符号上加双保险属性见 IOSDisplayShim.m__attribute__((used, visibility(default))) struct macdrv_functions_t macdrv_functions { ... };used让链接器保留符号visibility(default)让它进入导出表。源码注释里还记了一课某次误用 Release 构建后 6 个符号消失、Thumper 白屏——所以每次改构建配置后要用xcrun dyld_info -exports验证macdrv_functions还在导出表里。三、Swift 端直接挂在 UIWindow 上的 CAMetalLayer层的创建和管理在 ContentView.swift 的MetalHostView中这里有两个值得一提的工程决策① 不用 SwiftUI 托管直接挂在 UIWindow 上。在 iOS 26/27 上SwiftUI 托管视图的 backing layer 会被偶发路由到快照路径导致 Metal 提交被静默丢弃画面卡在旧帧。所以项目用一个进程级单例的普通UIViewlayerClass CAMetalLayer.self直接加到窗口上SwiftUI 里的MetalBackedView只留作透明的几何占位 触摸输入层触摸穿透到它处理。② 几个关键的层属性配置ContentView.swiftpixelFormat .bgra8Unorm、framebufferOnly true只用于屏幕呈现的标准配置displaySyncEnabled NO通过私有 API 把低帧率游戏的 present 从显示同步调度中摘出来——否则游戏只有 1 FPS 时大部分帧的presentedTime为 0画面看起来像卡死nominalFramesPerSecond跟随屏幕最高刷新率ProMotion 机型可到 120Hz帧率控制交给 FPSOverlay.swift 的 DisplayLink 完成注册时机在didMoveToWindow()中只发生一次if !Self.layerRegistered { Self.layerRegistered true madeira_display_set_layer(host.metalLayer) // 只注册这一次 }见 ContentView.swift。四、虚拟显示器布局与触摸共用一套数学Wine 客端渲染的其实是虚拟显示器会话启动时由环境变量MADEIRA_SCREEN_W/H播种未设置则 1024×768游戏调用ChangeDisplaySettings改分辨率时win32u 会回调winios_display_mode_changed()垫片层在主队列发出一条MadeiraDisplayModeChanged通知Swift 端收到后重新布局。完整流程见 IOSDisplayShim.m 与 GuestDisplay.swift。布局与触摸映射共用同一个GameSurfaceLayout结构GuestDisplay.swift支持 4 种模式模式效果Fit完整显示、居中留黑边Fill铺满屏幕、裁切溢出Stretch强制拉伸到整个屏幕Aspect按游戏实际回缓冲比例等比缩放显示层的 frame 和触摸坐标换算用的是同一套 rect 计算——这是刻意为之如果两边各自算比例Fill/Stretch 模式下一像素的舍入差就会让点击位置偏移。触摸点还会被钳制到客端画面边界内按在黑色边距上也不会产生越界坐标。五、顺带一提帧率钩子的弱符号兼容垫片层还定义了一个__attribute__((weak))的madeira_set_display_max_fpsIOSDisplayShim.m。带 30FPS 上限分支的 DXMT 会用强符号覆盖它不带该功能的 DXMT 则链接到这个空实现前端再据此隐藏对应设置项——同一份前端代码可以链接两种 DXMT无需改动。六、关键文件导航 想动手看源码按这个顺序效率最高驱动接口定义IOSDisplayShim.h垫片层实现macdrv 函数表 虚拟显示器IOSDisplayShim.mCAMetalLayer 宿主与注册时机ContentView.swift布局/触摸共用的几何计算GuestDisplay.swift桌面模式的多窗口合成器Winios/Winios.m整体架构背景ARCHITECTURE_ANALYSIS.md七、小结IOSDisplayShim 展示了移植工程中一个优雅的通用套路不要改造上游而是满足它的探测方式。DXMT 按符号名探测显示驱动垫片层就精确地伪造出它要的那 9 个函数把无数 Windows 窗口收敛成一块 iPhone 屏幕再配合 Swift 端对 CAMetalLayer 的精细调教窗口级托管、禁用显示同步、引用计数交接x86 游戏的每一帧最终都稳稳落在 iOS 的 Metal 渲染管线上。对于任何想在 iOS 上桥接非原生图形栈的项目这套符号级适配 单例渲染目标的做法都值得借鉴。【免费下载链接】MadeiraRun x86-64 Windows PC games on jailed iOS via FEX-Emu Wine DXMT项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表