# 1.4.3 Mac 适配与测试记录(2026-10-18 收尾) 本次仅测试 Mac。1.4.3 的资源合并、代码改写、实际游玩和唯一正式 App 打包已跑通,按用户最新“收尾、记录、提交”要求结束本轮。未完成的长局目标与后续项目如实保留;不将本轮计为已满 9 小时,也不承诺全游戏或稳定 30/60 FPS。标题或单张场景帧率不能代表长时间性能。 | 范围 | 当前结果 | 边界 | |---|---|---| | 资源与代码 | 指定 NSZ 合并、六层 IVFC、三个模块结构/语义及原生探针通过 | 仅精确哈希匹配的 1.4.3 | | 实际游玩 | 17 个有效环境点;读档、移动/跳跃、菜单/地图、保存重读、死亡重试、神庙进出、新游戏开场 | 未完成完整序章或全游戏流程 | | 显示 | 三态画质、四档补帧、键盘 1、真实窗口 zoom、4K drawable 通过 | 原生 2K 指合成画布/UI;4K 不是物理显示器测试 | | 桌面与打包 | 唯一入口、存档迁移/备份、实际游玩后严格 App 签名验证通过 | 代理测试使用独立 profile | | 持续运行 | 99bc 版 3 小时 21.65 秒后保存并关闭;最终 a431 版 30 分 31.68 秒,真实用户输入触发停止控制 | 两轮分别记录,不能合并冒充连续长局;4 小时 15 分钟及最终版 75 分钟目标未完成 | 本轮设备为 Apple M4、16 GiB、macOS 26.5.2。详细证据及失败尝试保留在下文;资源、存档、截图和日志不随源码提交。 ## 已验证 - 使用用户提供的 APFS 卷更新 NSZ,解压后逐个校验 NCA;按实际 ExeFS 哈希选择版本,不依赖文件名。 - 另从已解压更新的 Control NCA 提取 `control.nacp`,完整校验其六层 IVFC;`0x3060` 的版本字段确为 `1.4.3`,16,384 字节 NACP 的 SHA-256 为 `51b8838812b9f93f8739f77a679dc916bea277b6049dac39357aba8f969e28c1`。证据 `packaging/update-internal-version.json`,避免仅凭窗口标题认定版本。 - 直接使用 1.0.0 本体合并累计更新,全部六层 IVFC 验证通过。RomFS:17,052,625,232 字节,304,243 个文件,SHA-256 `797e66f2efa5b99bdafe4af2d6e778a5ef2f2ca5dc3b45136dd601daea419e9b`。 - 新主模块与 SDK 的 RELR 重定位已展开,加载时共应用 554,281 项重定位。 - 三模块独立结构检查、逐站点等价检查均通过;68,968 个语义站点、184,378 次有效试验、600 次破坏负对照。另在 Apple silicon 上以 8 线程执行真实改写后的排他原子片段,累计 320,000 次成功更新,负对照可检测错误。 - 1.1.2 主模块重新改写后与固定哈希相同。 - 四线程 4,000 次跨模块时钟调用通过;三个模块构造函数和原始 nnMain 启动检查通过。 - 新 SDK 的 GetInfo 28 返回没有额外 alias 保留区;新增 RELRO 只读权限及 PermissionLocked 处理。原生探针验证完整宿主页确实只读、4 KiB 逻辑边界、相邻可写页面保持可用、权限锁及无效请求拒绝。Mac 宿主页为 16 KiB,部分边缘页按四个客体页权限的并集处理。 - 补齐单一应用的 applet resource id、直接启动时无前序程序、无启动参数及无 DLC 列表的语义。 - REV15 音频支持 0x188 字节声音输入、原生浮点 biquad、0xa8 字节 splitter destination;保留旧版 Q14 路径。两次连续完整 wire 请求的结果与独立双精度采样预期一致;新旧音频探针共 46,587 项检查通过,破坏浮点路径的负对照失败符合预期。 - 更新导入/容器/打包等 23 项自动测试通过。 - 2026-10-18 本机 Apple M4 首次正常显示中文标题和菜单,标题显示约 30 FPS(shipping controller,首次着色器编译后)。此结果不代表场景帧率。 - 新版可见性统计着色器按实例读取标记;精确 Maxwell 哈希限定的转换保留每个有效调用加一的原子副作用。v143/v112 各 96 个真实 GPU 用例通过,覆盖不完整组、非零起始实例/顶点、无效输入及无符号溢出,双倍累加负对照被拒绝。v112 生成的 SPIR-V/MSL 和反射与之前缓存一致。 - 2026-10-18 实际读取旧存档进入监视堡垒鸟望台,角色、场景、UI 正常显示,声音有输出;首次观察约 27 FPS,仍使用 shipping frame controller(不代表最终性能)。 - 角色移动、跳跃、背包、地图、系统菜单和游戏内保存已实际验证。保存仅写入独立测试 profile;用户原存档未改动。 - 实时步长通过生产 C 代码生成实际修改和跳板,再用 Unicorn 执行原始计算;15/20/25/30/50/60/90/100/120 Hz 的 ratio、inverse ratio、seconds、整数 FPS 及全局 ratio 一致,固定步长和采样检查通过。新版控制器 77 项检查覆盖升/降档、冷却、回调否决、CPU/GPU 较慢者主导及原版负对照。已在新进程重新读取刚保存的副本进入场景,约 2 分钟未遇到启动或加载崩溃;个别画面显示约 75 FPS,角度/工作负载与原版帧控测试不同,不能据此作公平性能比较或稳定帧率承诺。 - 2K 输出必须同时修改新版显示配置的画布尺寸(`+0x10`)和 crop(`+0x508`)。仅改 crop 的早期方案已通过 GPU 记录判定无效:合成纹理仍是 1920×1080。修正后标题的 8 个颜色目标实际为 2560×1440;关闭后恢复为 7 个 1920×1080 合成目标,最终固定的 2560×1440 swapchain 保留。证据:`output-title-test-12/pass-native.txt`、同目录日志 F1448/F3108 及截图。 - 输出补丁的独立检查直接执行生产 C 生成的跳板和原有比例选择代码,覆盖 1080p→1440p→1080p、非等比显示比例、画布/crop 一致、场景相关字段未被覆盖及原版负对照;6 项通过。这里的“原生 2K”延续旧版本含义:游戏合成画布和 UI 为 2560×1440,3D 场景分辨率仍由游戏独立控制,并非保证所有场景内部通道都以 1440p 渲染。 - 2026-10-18:iOS 要把跳板签进只读的可执行段,两段跳板因此挪到代码段末尾的空白里(`TEXT_END` 向上取 16 字节对齐:先是时间步长的 4 个字,紧接着是输出的 7 个字)。两个检查改用运行时的同一算法算跳板位置,并另外检查两件事:跳板在只读数据(`RO_OFF`)之前结束,原映像里那一段全是 0。这比运行时只看 16 KiB 页边界更严。108 项和 6 项仍全部通过,改动的原字分别是 18 个和 28 个。 - 已读档进入监视堡垒并在原生 2K 下移动/跳跃;补帧四档完成一轮切换,30+30/半刷新率/不限帧均有实际生成帧。日志累计 1,794/1,800 帧成功插值,其余为首次输入不足或切档历史重置;当前只是该场景短测,不能保证所有场景和镜头运动的插值质量。 - 新版场景 EASU 放大着色器为 `ac58fc18...`,旧版 `a3a570eb...` 的精确哈希匹配不会覆盖它。新版先乘 `1/3.5`、滤波后再乘 `3.5`,两者抵消,不能额外乘一次增益。实际原着色器的 12 个 RGB/HDR 平色 GPU 用例验证通道和增益不变、范围上限 3.5;删掉后级增益的负对照被拒绝。新增精确哈希限定路径使用 MetalFX HDR 处理,保留全部目标、混合、深度、视口和常量检查;`manual-metalfx-test-13` 已实际运行 1600×896→1920×1080,窗口约 1815×1017(低于 1080p)也能手动启用。 - 统一实际键盘和诊断输入的按键 1 处理,按下沿切换,长按重复事件不会反复切换。`manual-metalfx-test-13/hints-hidden.png` 确认 FPS、着色器和底部负载提示同时消失,游戏 HUD 正常保留。 - 真实打包 App 已启动并读取测试副本进入场景,首次启动完整验证 17,052,625,232 字节 RomFS,布局、重试、2K 堆三个覆盖文件都通过校验。其中重试那份在 1.4.3 上不起作用:游戏读的是任天堂自己改过的 `DmF_SY_GameOver.110`(等待用 `EventWaitUpdate`,不会被暂停冻住),覆盖文件改的是保留下来的 1.0.0 那份,游戏不读它。包内只含 v143 模块/数据、一份运行时,翻译器和模块与构建源逐字节一致。实际启动器生成的 `profile-before-1.4.3` 与原测试 profile 的全部 54 个文件一致。 - `packaged-4k-test-14` 用 1280×720 窗口与诊断 drawable scale 3 生成真实 3840×2160 输出纹理(非物理 4K 显示器测试)。默认 MetalFX 两阶段均关闭;手动开启后场景 1600×896→1920×1080、出图 1920×1080→3840×2160 都实际编码。出图放大累计至少 4,800 次,GPU p50 1.68 ms / p95 1.85 ms(该机器、该场景短测)。原生 2K 画布加补帧也正常运行,296/300、596/600 的插值结果只因初始历史/输入未准备而少量跳过。 - 最新构建再通过 v143 两次完整 SDK/构造函数/nnMain 前缀检查、4 线程 4,000 次跨模块调用、内存权限和 46,587 项音频检查及音频负对照。v112 重用字节一致的旧模块后,重新构建共用运行时,以上基础原生探针和两次启动检查也通过。根目录 23 项自动测试通过(使用项目虚拟环境,系统 Python 未安装构建依赖)。 - 完整 `build_app.py` 已使用指定的真实本体 XCI 和 1.4.3 NSZ 在独立工作目录跑通:实际 NSZ 解压/NCA 校验、按哈希识别、ExeFS 提取、覆盖文件生成、编译和打包。复用了已验证的 RomFS/代码缓存,约 3 分钟完成,其中 macOS 首检 155 秒。该次最终 App 的跨模块、内存和音频探针通过;不是一次无缓存的完整解密测试。证据:`packaging/full-build.log`、`full-script-validation.json`。 - 现有桌面 `Tears of the Kingdom Mac.app` 最终改为唯一 `MacNativeGames/out/Tears of the Kingdom.app` 的符号链接,直接采用正式 App 的签名和启动身份。用户存档完整复制到 `~/Library/Application Support/MacNativeGames/TOTK/profile`,旁边备份 `profile-before-1.4.3`;54 文件逐一校验一致。原 `native-port/analysis/phase6-profile` 及旧备份保留不改。旧桌面启动器归档为没有 `.app` 后缀的目录,不增加第二个入口。测试使用另建副本;新启动的用户进程仍会按同一 game-slot 规则优先于代理测试。 - `fall-test-16` 已完成一次真实高空坠落死亡 → Game Over → 选择“重试” → 重新读取存档返回场景。开启原生实时步长和 FPS 补丁;进度文件读取由 2 次增至 4 次,截图验证回到滑翔场景。测试仅在副本中修改两个已知存档位置向量,原始 54 文件哈希保持一致;新版实际读取的是保留不改的 `DmF_SY_GameOver.110` 流程。 - 世界 HUD 时钟:默认不补帧及 30+30 分别在 120.334 / 120.294 秒内推进 120 游戏分钟。另作 `TOTK_REALTIME=0` 对照也推进正常(120.308 秒/120 游戏分钟),因此 HUD 时钟不是区分补丁是否必要或有效的负对照,不能据此声称实时补丁改善了模拟速度;它只确认这些实测模式没有明显时间加速。动画、物理和特殊时间缩放仍须分别检查。原来的 `shot` 包含固定 2K swapchain 的未用区域,时钟检测改用 `wshot`;720p 明亮背景的 OCR 需要紧裁剪和放大,失败的初始读数是测试工具问题。 - 进一步静态检查发现,FrameRateMgr 的两个调用点都来自 `3e5658` 的步长生产路径,前面还有 `sys+0x1da` bit 0 选择的 0.5/1.0 系数。初版 callee 入口替换会覆盖该系数,已改为在乘系数前替换名义 60/FPS 值,保留原乘法、两个调用点和 FrameRateMgr 本体。跳板使用下条 LDXR 会覆盖的 x8,避免破坏仍存任务标志的 x9。生产 C/Unicorn 的 108 项检查覆盖 9 种实际帧率、3 种名义 FPS、普通/半步和两个调用入口,以及原版负对照;输出补丁检查也再次通过。随后重新构建并完成下述场景测试;`world-tour-19` 在哈特诺前主动中断,前两点记录属于初版时间补丁。 - Metal API Validation 下,生产 `mfxup_encode_ex` 以游戏实际的 R11G11B10 float 格式完成 11 个 0..3.5 RGB/HDR 平色用例,通道和增益保持正确。证据:`upscale-native/production-hdr-gpu.log/.json`。此检查针对色彩范围和增益,不要求 MetalFX 与原 EASU 滤波逐像素相同。 - 修正后的 producer 时间跳板已完成完整 CLI 打包,该渲染/时间补丁构建的运行时 SHA-256 为 `4f227ad4890091ac16f9c71f7a611660a782e91fea4d04ccdffec0f45e000f4a`,证据 `packaging/final-producer-build.log`。`world-tour-20` 从此构建重新开始场景巡测,旧巡测不冒充该构建的结果。后续仅启动/资源缓存写入修正的构建另行记录。 - 重复构建暴露并修复了工作目录隔离错误:rsync 的目录尾斜杠排除规则漏过源码树中的 `tooling` 符号链接,随后写子链接会穿透到共享缓存。本次触发覆盖的 SPIRV-Cross 源码缓存已恢复为固定提交 `aa217aeb6c9f0ace7a0ab233b28807edf45eb165` 的干净真实目录;既有编译器二进制、游戏数据和存档未改动。构建器现在排除目录及同名符号链接,先拆除旧工作目录父链接,再建立私有工具目录;损坏缓存链接只删除链接自身。增加 3 项真实隔离回归,根目录现共 26 项通过。 - 从恢复后的干净源码重新编译 SPIRV-Cross,与保留的原二进制对 66 个本地 SPIR-V(包含新旧可见性计数与放大着色器)按生产参数分别翻译,全部成功且输出逐字节相同。再次执行实际 `prepare_work` 后,两份独立源码缓存的提交和跟踪文件状态均未变化,工作工具目录为私有真实目录。证据:`spvc-source-validation/translation-parity.json`、`packaging/repeated-work-preparation.json`。此恢复只保证官方固定版本,不声称恢复未知的本地源码改动。 - 最终 producer 构建的包内运行时重新通过跨模块调用、内存权限、46,587 项音频检查、音频破坏负对照、两次原始 nnMain 前缀启动及地址身份负对照;证据 `packaging/final-package-native-validation.json`。 - `world-tour-20` 首轮 17 点全部正常退出,每点核对 8 张截图和三态画质切换。有效环境为 7 个城镇、5 个地底树根、3 个空岛神庙落地点;`cave_085` 实际在岩坡、`cave_089` 实际在古代石柱室外,明确不算洞穴覆盖。利特村点为商店内部;空岛点未进入神庙内部。每点另存 `visual-review.json`,不会将仅仅截图成功自动当成验收通过。 - 本轮设备为 Apple M4、16 GiB、macOS 26.5.2,窗口 drawable 1280×720、默认游戏画布 1920×1080;每点热身后固定镜头取样 30 秒。15 个有效点按平均整帧耗时换算约 83.9–110.2 FPS,**不代表稳定帧率或公平版本对比**。地底 CheckPoint001 首次转镜头出现 902.4 ms 长帧,同时新建 8 个着色器库 / 6 条管线;空岛 Dungeon015 的截图也有瞬时 35 FPS。固定镜头平均值会掩盖这些卡顿,必须连同首次转镜头日志一起看。汇总 `world-tour-20/summary.json`,后续重进的缓存效果记录见下一条。 - 卡顿点重进:`world-tour-21/depth_CheckPoint001` 在相同点位和镜头/画质指令下,固定段约 101.4 游戏帧/秒;后续转镜头及切画质最大整帧 63.143 ms,超过 100 ms 的帧由首次的 5 个变成 0 个。首次卡顿改变了准确镜头轨迹,不能把这当成严格实现 A/B 或普遍提帧;它证实这次访问的缓存复用消除了所见的大编译卡顿。证据 `world-tour-21/depth-revisit-comparison.json`。 - 补测的 `cave_Dungeon014`、`cave_Dungeon032` 确认角色站在真实洞穴内部神庙平台,岩壁/洞顶、雾气、水滴、入口特效和三态画质均正常;分别在 `world-tour-21`、`world-tour-27`。默认巡测表已用它们替换两个室外候选。额外 `cave_Dungeon027` 处于游泳状态,只保留为水面样本,不算正常洞穴落地通过;原始失败点和截图均保留。 - 实际移动速度:同一独立旧存档基线,S 键 2000 ms、松键后等待、通过游戏菜单手动保存,再只读解析新存档的两个位置向量。默认高帧率的位移 10.4780 m(5 秒移动/静止混合段约 79.8 FPS),30+30 为 10.6098 m(约 30.0 游戏 FPS),差 1.26%;约 60 FPS 的显式节拍参考为 10.5251 m。故这条短路径未见明显速度随帧率变化。 - 移动负对照:在约 78.2 FPS 时故意将步长固定为 16.667 ms,同样输入移动 13.1015 m,比默认多 25.04%,证明这项距离检查能发现明显错误速度。`TOTK_REALTIME=0` 的尝试实际仍受显示节拍约束在 60 FPS,位移 10.4284 m,不能用它否定高帧率补丁必要性。最初读取器错误地要求“移动中自动存档”两个向量也相等,已保留该失败记录,修正为读取保存菜单之后最新的手动存档;游戏副本保存正常。证据 `walk-comparison.json`、`walk-*-22..26`,本地诊断过程保存于 `procedures`。仅覆盖这条路径,不能替代全部物理、动画和过场验证。 - 最终 producer 构建在 `fall-final-28` 再次完成坠落死亡 → 实际 Game Over 菜单 → 重试 → 返回高空滑翔存档场景。未开启文件打开跟踪,因此 `progress.sav` 日志提及次数没有变化,不把它当作重读计数证据;使用前后实际画面确认。证据 `retry-validation.json` 和三张截图。 - 恢复的 SPIRV-Cross 源码重建进一步与原二进制对当前全部 2,798 个缓存 SPIR-V 按生产参数进行翻译,全部成功、输出一致(14.5 秒);证据 `spvc-source-validation/all-cached-translation-parity.json`。范围为本机已收集的着色器,不是全游戏着色器集合。 - 进一步发现原构建器只有链接器级可执行文件签名,没有完整 App 资源签名;现新增资源封装签名及严格验证,签名失败时保留之前 App 的回归通过,根目录共 27 项测试通过。同时修正游戏槽初始化:显式外部槽不再创建 App 内的 `analysis` 目录。真实 0555 只读安装目录测试中新代码正常使用外部槽退出、旧代码因 EACCES 失败,均未启动客体游戏;证据 `packaging/readonly-slot-validation.json`。后续真实桌面启动与资源签名复查结果见 99bc 及最终版记录。 - 加入桌面入口迁移的 4 项回归,根目录共 31 项通过:旧存档复制/备份、重复安装保留新进度、冲突不覆盖、桌面本身为正式 App 时不自链接。正式 App 资源签名通过后,启动器直接执行 `--check` 正常;旧桥接入口经签名变更后触发了可移动卷授权身份问题,没有修改系统隐私设置,改用同一个正式 App 的桌面链接和标准用户数据目录解决。实际 LaunchServices 启动不再访问旧外部存档路径;两个旧 game-slot 路径均链接到标准目录中的同一个锁。 - `desktop-shrine-29` 使用签名运行时 `98cab94a0b403888a04ad43066df7d3ab1a753570a7c7d6febdf762cb4fcdf5f`,从真实桌面链接进入 Dungeon038 神庙内部,走路/跳跃后经入口返回空岛。物理 1920×1080/60Hz 显示器上,窗口 zoom 后 drawable 为 1808×1017,MetalFX 默认关闭;手动 MetalFX、原生 2K、关闭均通过,还原 1280×720 后仍可手动开启 MetalFX。真实键盘 1 同时隐藏中央说明/FPS/着色器/底部负载并可恢复,游戏 HUD 保留。重复打开桌面入口仍为同一 PID。不是物理 2K/4K 显示器测试。 - **游玩后签名复查发现并修复另一处写入:** 单文件 `verify_image()` 没有采用 `TOTK_ROMFS_PROOF_DIR`,在 App 内新增 `.romfs-verified-v1`,严格签名验证报 `file added`;分块路径此前已支持外部缓存。现统一尊重非空外部目录。`scripts/verify_romfs_proof.py` 用生产验证代码和微型生成资源分别检查单文件/分块的完整验证、缓存复用、强制验证、内容变化拒绝,以及只读资源目录不增加文件;全部通过。本次开始时的旧实现(`b75994f`)作为负对照在“缓存必须在外部目录”检查失败。首次签名失败记录保留;后续 99bc 版的实际游玩后签名复查通过。 - `fresh-newgame-30` 从空目录经真实桌面入口开始,开场林克/塞尔达同行、海拉鲁城堡地下地点名称/移动教学正常,实际移动、转镜头、攀爬及自动保存通过,产生两份 `progress.sav`;尚未完成完整序章。该轮用上述 98ca 构建,随后正常关闭用于替换缓存路径修复版。所有输入和存档仅在隔离副本;本次没有借用旧存档冒充新游戏。 - 资源缓存修复版运行时为 `99bc19e0aeb980478d91df80d39c3a4e609ff7c672fcd303161435e0bae13e3b`,翻译器仍为 `6dd53d6486c96e3a95ae5fd49fdc35f75f6e603ff8ededa3d7250fd2e4fe1220`。重新签名/严格资源验证和 macOS 启动检查通过。`fresh-reload-31` 经真实桌面入口,从标题列表选择刚才新游戏自动存档并返回地下场景,跳跃和 130 秒全部画质/补帧切换正常,7 张截图已核对。音频混音非零,underrun 由 7 增至 9、overrun 0;不是无掉音承诺。 - **99bc 包实际游玩后,严格 `codesign --verify --deep --strict` 通过。** 单文件校验缓存位于标准用户目录,包内 `analysis` 和 `.romfs-verified-v1` 均不存在。包内最终运行时另通过跨模块、内存、46,587 音频项、两个 FPS 设置的原始 nnMain 前缀检查,音频及地址身份负对照按预期失败。证据:`packaging/postplay-proof-fix-signature.json`、`proof-fix-native-validation.json`、`fresh-reload-31/visual-review.json`。 - 启动已有的红色诊断仍保留:vpool 对齐/来源拒绝、预排布纹理 CPU 回退、无附件通道、一个片元着色器没有 colour-0 可复制到补帧 UI 层。同类信息也出现在 `world-tour-20`,不能仅凭画面正常将它们认定为无害。`fresh-reload-31` 的这些行都早于监测起点;后续长局报告分别保留启动前诊断和监测期间新增诊断,避免混为一谈。 - 长局前半小时已确认更多首次编译卡顿:帧 121464 记录整帧 936.2 ms、GPU 10.2 ms,同时新建 2 条管线/2 个着色器库(日志创建耗时累计 1000.0 ms);另有 370.4 / 333.7 / 288.2 ms 并伴随新建库/管线。故缓存已热的平均帧率不能代表初次探索体验。`interim-longframe-diagnostics.json` 仅统计原生日志中的相对尖峰,不是完整帧时间分布;持续超过预算但没有高于近况四成的帧不会全部记入,不能据此计算真实 p95/p99。长局最终统计见 `long-soak-32/final-summary.json`,其中超过 100 ms 的相对尖峰为 18 次、超过 500 ms 为 4 次,含一次诊断工具窗口重叠;不是完整帧分布。 - 长局内存出现阶梯增长,不能宣称无泄漏:补采开始约 5.15 GiB,约 59 分钟曾到 5.65 GiB 后回落,约 125 分钟到 6.25 GiB;期间切换了画布和补帧,并发现了新着色器,尚未隔离原因。一次 `vmmap -summary` 快照保存在 `vmmap-125min.txt`,同时保留命令时间窗口,分析长帧时须标注诊断工具影响。该快照中的 malloc 已分配约 1.4 GiB、图形 owned-unmapped 约 1.4 GiB,包含大量非驻留/交换页;不能仅依据 RSS 约 1.7–1.9 GiB 推断总占用。后续应在同一场景、同一热缓存和固定模式下分别比较保留对象、图形缓存与旧版本;当前数据不支持归因于某一功能或版本。 - 切入原生 2K 的三个重复观察窗口(75/120/165 分钟)中,footprint 分别增加约 138.3/140.8/137.2 MiB;相邻渲染器记录的 Metal 已分配内存分别为 2925→3061、3038→3174、3149→3282 MiB。游戏内存池数量没有对应的大幅增加,登记位置统计也未解释全部增量。该相关性是后续追查画布/补帧重建保留资源的线索,不是泄漏或具体责任模块的因果证明;这些周期记录与 footprint 不是严格同时采样。原始前后帧号和资源登记行保留在 `long-soak-32/interim-picture-memory-transitions.json`。 - GPU 计时日志存在异常样本:至帧 521100 的 300 帧窗口中,整帧 p50 33.28 / p95 35.20 / 最大 46.17 ms,但 GPU 最大值为 33158.33 ms。相邻窗口又回到约 11–13 ms。须进一步校核时间戳、跨提交范围和回读生命周期;目前不将这种异常最大值当作一次实际卡住 33 秒的证明,也不据此宣布 GPU 性能改善。原始日志保持不改。 ## 补帧配置重复构建的内存对照 `long-soak-32` 实际观测 10,821.65 秒(3 小时 21.65 秒),因上述重复增长主动转入针对性排查;原定 15,300 秒没有完成。19 张周期截图核对正常,监测期间没有新增红色诊断,音频 underrun 从 9 到 10、overrun 为 0。随后在游戏内手动保存,`slot_01/progress.sav`、footprint/caption 等数据更新,再正常关闭测试进程。该 99bc 包在这次实际游玩后严格签名验证再次通过。 首先测试了两个没有效果的方向:局部 autorelease pool、复用同一个 Metal 4 compiler。12 次构建时,两种 pool 方式及共享 compiler 的 Metal 分配都仍达到约 667.8 MiB;弱引用确认旧 interpolator 对象已释放。这些初步实验固定输入 1600×896,仅交替输出 1080p/1440p,不能与下表不同输入尺寸直接作数值 A/B;它们否定了这两个简单修改能解决当前构建增长的猜测。正式出图入口另设 pool 管理每帧临时对象,不把它当作解决重复构建增长的证据;共享 compiler 修改没有采用。 随后直接使用生产 `totk_mfx.m`,按实际游戏的两组尺寸交替 12 次:1600×896→1920×1080、2128×1200→2560×1440。同一个诊断二进制仅切换缓存开关: | 配置 | Metal 分配观察 | 范围 | |---|---|---| | `TOTK_MFX_FI_CACHE=0` | 第 2 次约 223.75 MiB,第 12 次 785.67 MiB | 每轮重新构建 | | 默认配置复用 | 热身后在约 174.78–224.00 MiB 往返 | 同配置复用后未逐轮增加 | 探针只创建资源,不编码插值;其 footprint 远小于游戏的实际占用,不能把这些 Metal 分配差值直接换算成整局实际 RAM 节省。完整释放登记资源、外层 pool 结束、空命令缓冲完成并等待 250 ms 后的样本也保留。证据为 `long-soak-32/mfx-resize-*.jsonl`、`mfx-resize-construction-validation.json`;私有对照源码保存在 `procedures`。 正式实现最多保留 4 个精确匹配输入/输出尺寸及颜色格式的 interpolator,不驱逐旧项,满容量后沿用原构建路径,避免已有配置被反复替换/重建。5 种配置反复切换的原生探针确认容量没有超过 4,第一项没有被替换,第五种可以继续构建。配置切换仍清除 `prev_ok`/`hist_ok`,恢复编码时通过 `shouldResetHistory` 使历史失效,遵守 [Apple 对历史数据连续性的要求](https://developer.apple.com/documentation/metalfx/mtlfxframeinterpolatorbase/shouldresethistory)。未命中的新配置仍可能有增长;这不是所有长期内存问题的修复。 资源生命周期随后补齐:插值编码返回 8 项帧资源(interpolator、两张颜色图、深度、motion、UI、输出、参数 buffer),由 GPU 完成回调持有;编码后清空 interpolator 的公开纹理属性,避免配置缓存长期引用旧场景资源。出图入口的临时对象池与正常提交/出图路径一致,不缩短显式完成回调的持有期。 `MFX_RESIZE_ENCODE=1` 使生产探针实际执行 12 次配置切换、每次两帧(历史重置/连续历史),在 Metal API Validation 下共 24 次编码及黑色场景读回通过;公开纹理绑定已经清空,完成回调资源数为 8。相同二进制的开启/关闭缓存对照,热身后开启时 Metal 分配在约 180.77–234.80 MiB 往返,关闭时逐轮升到 796.72 MiB;其 footprint 分别约 430.66 / 989.22 MiB(探针,不是整局节省承诺)。证据:`mfx-lifetime-encoded*.jsonl/.log`、`mfx-lifetime-encoded-validation.json`。 仅配置缓存的中间构建 `9a543294...` 完成封装/签名/启动检查,但没有用它冒充实际游玩验证。最终资源生命周期运行时为 `a431687be9d400335ba314b88b658886ca4e4762b3470c89e0ede88d77c5d5e5`;正式 App 已封装、签名并通过真实启动器检查。最终版实际游玩结果如下;前一轮三小时结果不直接算作这项修改的验收。 ## 最终构建与收尾结果 最终运行时为上述 `a431687b...`,App 为 `MacNativeGames/out/Tears of the Kingdom.app`,桌面原入口直接链接到它,未增加第二个入口。根目录 31 项测试通过;包内跨模块调用、内存权限、46,587 项音频检查及音频/身份负对照通过。证据 `packaging/fi-cache-source-tests-31.log`、`fi-lifetime-native-validation.json`、`fi-lifetime-package.json`。 `fi-cache-regression-33` 经真实桌面入口启动,先逐一核对三小时长局保存的 54 个文件,再从保存列表读取 07:57 的监视堡垒存档,重新进入场景、实际跳跃通过。最终版监测 1,831.68 秒(30 分 31.68 秒),62 次周期采样、7 张截图均已人工核对;昼夜/天气变化、角色、地形和 HUD 正常,没有新增红色诊断,启动已有的 14 行诊断另列。期间三态画质按一分钟切换,补帧经过 30+30、半刷新率、不限帧;此轮尚未到达预定的关闭档。四档补帧短测已有记录,不能算作这轮全部档位长测。 真实键盘输入触发监测器退出并停止所有自动按键;游戏 PID 37441 保留运行,未保存、关闭或替换用户正在接管的这局。原定最终版 75 分钟没有完成。**当前窗口仍使用 `~/Library/Application Support/MacNativeGames/TOTK/tests/fi-cache-regression-33/profile` 测试副本;正常关闭后从原桌面入口重开才使用正式 profile。** 未把测试进度迁入正式存档。历史复测方法中的“交付前关闭代理局”在本次被真实用户接管和最新收尾要求取代。 最终版 footprint 从 4.547 GiB 到 5.247 GiB,进程生存期峰值 5.312 GiB;不能据此宣布整局无增长。第 5/25 分钟附近的原生 2K Metal 分配约 3097/3102 MiB,且日志记录配置复用,与旧版每次相同切档增加约 133–136 MiB 的现象不同;这支持相同配置复用有效,但不是严格整局内存 A/B。受控 GPU 探针对照与整局观察分开报告。 最终版仍出现首次编译长帧:754.8 ms,同时创建 5 条管线/8 个库(创建累计 909.2 ms);另有无新管线/库的 219.1 / 187.1 ms 长帧。相对尖峰日志中超过 100 ms 有 7 次,超过 500 ms 有 1 次,不代表完整帧时间统计。音频 underrun 从 5 增至 14、overrun 保持 0,采样 RMS 非零;约第 29 分钟附近的欠载及长帧原因未隔离,不宣称音频完全无中断。停止附近系统快照为可用内存 35%、交换使用约 7818.75 MiB,仅记录环境,不作为原因证明。证据 `final-summary.json`、`audio-underrun-29min.json`、`interim-25min-renderer-memory.json`;只读复制的 `game.log` 标明快照时刻,统计限制在监测首末帧范围。 收尾只读核对标准正式 profile、其升级前备份、旧目录 profile 及旧备份,四个目录各 54 文件全部与迁移清单一致;未还原或覆盖存档。结果 `packaging/final-user-profile-validation.json`。最终 App 游玩后严格签名复查另见 `packaging/final-a431-signature.json`。游戏资源、密钥、测试存档、截图和日志均不纳入提交。 ## 本地证据和复测 历史记录原位于 `games/totk/analysis/v143-port`;2026-10-18 清理后,剩余原始报告、日志、截图和生成表已整体归档到项目外 `/Volumes/mac-APFS/MacNativeGames-artifacts/2026-10-18/analysis/v143-port`,原项目路径不再保留。重复构建和测试存档副本已先行清理,不提交游戏数据或密钥。 ```sh cd games/totk make -j2 runtime-v143 build/totk-native-v143 rewritten/v143 probe build/totk-native-v143 rewritten/v143 probe-memory build/totk-native-v143 rewritten/v143 probe-audio TOTK_AUDIO_REV15_NEGATIVE=1 build/totk-native-v143 rewritten/v143 probe-audio # 应失败 .venv/bin/python scripts/verify_v143_timing.py .venv/bin/python scripts/verify_v143_fps60.py .venv/bin/python scripts/verify_v143_output.py .venv/bin/python scripts/verify_v143_upscale.py .venv/bin/python scripts/verify_romfs_proof.py ``` 音频负对照必须失败,错误应为 `REV15 float voice and splitter samples`。启动检查使用 `TOTK_VERIFY_STARTUP=1`,停止在原始 nnMain 的 focus setup,未执行场景。 标题测试使用独立 profile 副本,`TOTK_FPS60=0`、`TOTK_GPU_ALLOW_SUBMIT=1`、`TOTK_GPU_WIDEN=1`、`TOTK_GPU_BLOCKRT=1`、`TOTK_GPU_MIDCLEAR=1`,显卡预算 5000 MiB。通过 `TOTK_INPUT_FILE` 输入 `key 38 150` 等命令,`wshot <绝对路径>` 保存窗口画面。所有游戏/GPU 测试遵守单实例 game-slot,不并行运行两份游戏。 地区复测使用 `scripts/mac_v143_smoke.py`:`--profile` 指向独立的旧版存档基线,`--output` 每轮使用新目录,`--slot` 必须与现有桌面入口使用同一个游戏槽;不能另建槽绕过单实例保护。脚本默认测试实际打包 App,逐点复制并只修改两个已知位置向量,读档后热身 25 秒、固定镜头 30 秒、转三次镜头并切换三态画质。日志、原始帧数据(退出后 gzip)、截图和运行时哈希均保存。坐标只是候选,必须核对地面、室内外及遮挡;旧报告会被保留,不能用重复目录冒充复测。后续脚本还记录单调时钟窗口和实际游戏帧数/秒,便于与耗时均值交叉核对。 HUD 时钟复测使用 `scripts/verify_running_clock.py`,挂接已经持有同一游戏槽的精确测试进程,不负责启动/移动/保存/关闭;看到真实用户输入即停止。默认间隔 120 秒,最长 1200 秒,避免超过完整游戏日后出现两张 HUD 截图无法辨别的日数回绕。它只检查世界时钟,不代替移动、物理、动画或过场验证。 长局监测使用 `scripts/mac_v143_soak.py --pid <测试进程> --test-root <独立测试目录> --duration 14400`,先确认已进入可游玩场景、初始画质和补帧均关闭。目录必须有 `launch.json` 中标明 `TOTK_SLOT_OWNER=codex`、同目录 profile 和 input.txt;实际桌面启动的可写路径使用标准用户数据目录下的 `tests`,不能引用外部卷中的旧存档路径。长局禁用 `DM_NVN_TSV`,每分钟保存 RSS/CPU、最近游戏帧号、音频 RMS/峰值及累计 underrun/overrun;帧号来自每 300 帧的周期日志,短窗口帧率只能近似。每 10 分钟截图/短转镜头、每 15 分钟切画质、每小时切补帧;这些间隔可通过参数缩短作监测器实测。槽身份、进程或日志被替换、真实用户输入、90 秒没有周期帧推进时停止控制,保留现场。完成运行仍须人工检查截图、音频变化和日志,不能仅以脚本退出当成全游戏通过;完成后关闭自己的测试局,再严格复查 App 签名。 真实桌面测试的重开行为:普通 `open` 同一正在运行的 App 会由 LaunchServices 激活现有窗口,实测仍是原来的测试 PID,不会另外执行启动器或切换到正式 profile。game-slot 的用户抢占只在确实启动新用户进程时发生。真实输入会让监测器停止,但当前窗口仍属于独立存档;因此测试标题必须清楚标注副本;未发生用户接管时,应正常关闭代理测试再交付。若真实用户已接管,立即停止控制并保留当前窗口,明确告知它仍为测试存档副本,不能擅自关闭或切换 profile。 Mac 内存补采:`xcrun clang -O2 -Wall -Wextra -Werror scripts/mac_process_memory.c -o /tmp/totk-memory`,随后 `/tmp/totk-memory `。字段直接来自本机 SDK 的 `proc_pid_rusage(RUSAGE_INFO_V4)`,分别保留 `resident_size`、`phys_footprint` 和进程生存期的 `lifetime_max_phys_footprint`,不将 RSS 当成其他内存指标的替代。第二参数使用同一游戏槽的启动秒数,PID 重用/不匹配时拒绝采样;真实正向和错误启动时间负对照通过。本轮约 44 分钟后开始每分钟补采到 `process-memory.jsonl`,不能冒充从零时刻采集的完整曲线;首次 footprint 约 5.15 GiB、当时全程峰值约 5.21 GiB,RSS 约 1.70 GiB。进程全程峰值仍覆盖更早的时间。 渲染器日志 `内存…常驻…MB` 的历史标签实际由 `hle_nvn.c` 的 `TASK_VM_INFO.phys_footprint` 提供,不是 `ps` RSS;本轮独立采样按真实字段名记录,避免混用。 补帧重建/帧资源复测(在 `games/totk` 下编译): ```sh xcrun clang -fobjc-arc -O2 -Wall -Wextra -Werror \ -Iruntime -Ithird_party/d2r_gfx/gfx/nvn \ scripts/mfx_resize_probe.m runtime/game_slot.c \ -framework Foundation -framework Metal -framework MetalFX -framework QuartzCore \ -o /tmp/totk-fi-resize TOTK_GAME_SLOT_DIR="$HOME/Library/Application Support/MacNativeGames/TOTK/game-slot" \ TOTK_SLOT_OWNER=codex MFX_RESIZE_UNPOOLED=1 MFX_RESIZE_ENCODE=1 \ MTL_DEBUG_LAYER=1 /tmp/totk-fi-resize ``` 该探针会取得同一个游戏槽;未设置 `MFX_RESIZE_ENCODE` 时只检查资源构建,设置时额外检查真实编码、完成回调持有和黑色读回。用同一命令额外设置 `TOTK_MFX_FI_CACHE=0` 作无缓存对照。结果是受控小场景,不代替正式 App 游玩。所有 GPU 实验均串行,用户进程优先。 只读汇总使用 `scripts/summarize_mac_soak.py --memory --log --instrumentation `,输出 JSON;每项输入记录本次实际解析字节的 SHA-256。它仅按监测首末帧范围统计相对长帧,画质/补帧请求前后一分钟排除于分组计数之外,分组速率仅为粗粒度游戏帧数/秒。模式标签需与运行时切换日志核对,不将不同天气、时间、镜头或缓存状态的分组用于因果性能比较。诊断时间窗口按相邻分钟采样及 300 帧粒度保守标注,不证明工具导致了其中每个尖峰。本轮帧 453049 的 1600.2 ms 位于 `vmmap` 对应范围;936.2 ms 和 704.9 ms 两个编译尖峰位于该范围之外,仍须记录为实际观察。 ## 1.4.3 场景渲染分辨率(TOTK_RENDER_RES,外部贡献移植) 1.0.0/1.1.2 的 `TOTK_RENDER_RES=x` 已移植到 1.4.3:只改场景渲染尺寸,UI 与输出分辨率不变;未设置或为 `0` 时不打补丁,走游戏自身尺寸(手持 1280x720 / 座机 1600x900)。 1.4.3 的代码形状与 1.0.0 不同:两个维度都经 `s0` 转换,且 `w9` 在补丁点之后仍被 `main+0x585e64`(输出尺寸)使用,因此不能用 `x9` 做 adrp,改用本函数内纯临时的 `x3`。 | 地址 | 原指令 | 补丁后 | | --- | --- | --- | | `main+0x585e48` | `scvtf s0, w11` | `adrp x3, <数据页>` | | `main+0x585e50` | `str s0, [x19, #0xcc4]` | `ldp s0, s1, [x3, #16]` | | `main+0x585e54` | `scvtf s0, w10` | `str s0, [x19, #0xcc4]` | | `main+0x585e60` | `str s0, [x19, #0xcc8]` | `str s1, [x19, #0xcc8]` | 所有选择渲染尺寸的分支都汇合于 `0x585e48`(`0x585f50` 的分支只重新装载 w11/w10 后跳回)。值写在与实时步长补丁共用的数据页 `+16`。四处原始指令在写入前逐一校验,不匹配即 `runtime_fail("render resolution patch: main does not match 1.4.3")`,不会半途破坏主模块。 验证:分支与寄存器活性分析(`x3` 在整个函数内未被读写,`x9` 活性确认)、原始指令逐一比对、Unicorn 仿真(补丁前 1600x900 / 输出 1920;补丁后 960x540 / 输出仍 1920),以及本机 1.4.3 实机 `probe` 运行(日志出现 `1.4.3 render resolution patch: scene render size 960x540`,模块自检 `{"status":"pass"}`)。示例:`TOTK_RENDER_RES=1280x720`。Mac 默认不设,画面和以前一样(项目规则:Mac 上不降分辨率,这个变量只给想自己调的人用)。 iPhone / iPad 和 IPA 导出(合并时补上,和 `game_patches.c` 的做法一样):手机上不能改代码,补丁只能在 Mac 上导出时编进去。导出时设了 `TOTK_RENDER_RES` 才会编进去;手机运行时先核对编进去的指令,再把尺寸写到数据页:手机自己设的 `TOTK_RENDER_RES`,没设就用游戏的掌机尺寸 1280x720。导出时没编进补丁的,手机上照旧用游戏自己的尺寸,设了变量也不会去改代码。 1.2.1 不需要另做:它走 `game_patches.c`,`game_version.h` 里已经有这三处地址,原指令和 1.0.0 相同。 合并时的检查(M4 Mac):`scripts/verify_v143_output.py` 加了渲染尺寸用例:不设时这四处不变;设 960x540 时正好改这四处,数据页 +16 是 960、540,Unicorn 跑补丁后的指令,存进 +0xcc4 / +0xcc8 的是 960、540,+0xcc0 的输出宽度仍是 1920;IPA 导出只在设了变量时编进补丁;`960x540x` 这种值直接报错(只放宽这一处解析,校验就失败)。shell 里留着的 `TOTK_RENDER_RES` 不会影响别的用例。iOS 的分支用 iOS SDK 编过(`-Werror`)。实机两局(存档副本,同一个神庙入口):不设变量时场景渲染目标是 1600x896(1.4.3 自己的尺寸),设 `1280x720` 时是 1280x720,半分辨率的跟着变成 640x360,输出画布仍是 1920x1080;两局截图正常,红字一样(13 行,都是原有的),音频 0 欠载。 画布宽于 1920 时(PR #86,2026-10-20 合并):游戏会把场景尺寸再乘上“输出宽度 ÷ 1920”,两个方向都乘(实测 2560×1440 画布上设 2560×1440 实际渲染 3408×1920,3440×1440 画布上 6160×2576),而且场景大于 1600×900 时 `DynamicTextureAllocatorMem` 不够,一两分钟后在分配器(`main+0x1c2d20` / `0x1c2f20`)里崩溃。现在数据页里写的是“要的尺寸 ÷ 画布倍数”,启动时和按 3 键换画布时(通知游戏之前)重新写,所以每种画布上渲染的都是要的尺寸。纹理堆、Graphics 和应用内存池按画布和场景各自的像素倍数一起放大,场景部分留 1.6 倍余量(这个余量是在除掉画布倍数之前量的,现在偏大,安全)。堆配置补丁是 `<画布>-scene-<场景>`(`make_output_res_overlay.py --scene`,只有 1.4.3),`build_app.py` 会生成 1920×1080 / 2560×1440 两种画布配 1920×1080 / 2560×1440 两种场景;别的组合启动时会停下,并打印生成它的命令。**画面变化**:画布宽于 1920 时,同一个 `TOTK_RENDER_RES` 比以前软,因为以前其实是按画布倍数超采样了;要更清楚就设更大的场景尺寸。合并时验证:2560×1440 画布 + 2560×1440 场景跑 3 分钟、中途两次按 3 键,没有崩溃,帧时间 p50 约 9 ms;`verify_v143_output.py` 和 `verify_v143_timing.py` 通过。 ## 1.4.3 固定分辨率(TOTK_FIXED_RES) 游戏的动态分辨率控制器(`main+0x584300`,每帧由渲染器更新函数 `main+0x58196c` 调用)用 24 个帧时间样本预测下一帧的开销,在 100 % / 90 % / 80 % 几档之间切换场景分辨率(3024×1890 画布上实测场景在 3024×1888、2712×1696、2416×1512 之间变化)。控制器把当前档的缩放比例作为 float 放在 `x0` 低 32 位返回,调用方把 `cc4 * 缩放 / cc0` 存进渲染器 `+0xccc`。 所有分支都从同一个结尾离开,把缩放比例送进返回值的只有一条指令:`main+0x5848a8`,`mov w9, w23`。设置 `TOTK_FIXED_RES=1` 时把它换成 `mov w9, #0x3f800000`(1.0f):控制器照常运行、照常选档,但每一档都是 100 %。`TOTK_RENDER_RES` 选的场景尺寸不变,只是不再动态缩小。原始指令在写入前核对,不匹配即 `runtime_fail("fixed resolution patch: main does not match 1.4.3")`;iPhone / iPad 的内嵌代码不能运行时改,只在 Mac 上提供。 验证(M5 Mac,1.4.3,3024×1890 画布、场景为画布尺寸):不设时,进入洞穴后很快降到 2712×1696;设了之后场景一直是 3024×1888。只在一个场景(地下洞穴)和一个画布尺寸上验证过。 ## 后续待做与待测 - 最终 a431 版完成独立长局复测;本次 30 分 31.68 秒因真实输入停止,不能算 75 分钟或 4 小时 15 分钟通过。旧长局的保存、新进程读取及跳跃已完成;后续复测仍须从独立副本开始。 - 新游戏完整序章、更多战斗/能力/过场和全地图推进;当前只覆盖开场移动/攀爬/自动存档、17 个有效环境及基本菜单、保存重读、死亡重试和神庙进出。 - 实时步长的更多动画/物理/特殊时间缩放验证;当前短距离移动及保留半步系数的探针不能代表全部游戏行为。 - 首次编译卡顿:按同一版本、路线、镜头和设置分别记录冷/热缓存,收集完整帧时间;不能用相对尖峰日志推算整局 p95/p99。与 1.0/1.1.2 的公平性能对比仍未完成。 - 内存增长:在最多 4 项配置复用及 GPU 完成回调持有已实测的基础上,分别固定关闭、MetalFX、原生 2K 和补帧模式,用同一热缓存/场景复测 footprint、保留对象及图形缓存;覆盖超过缓存容量的新尺寸,区分正常缓存增长、交换和实际泄漏。不能凭 RSS 或单张快照定因。 - 音频欠载与非编译长帧:复现最终版 underrun 5→14 的窗口,结合生产/消费队列和宿主调度查因;区分首次编译、资源上传、交换及音频线程问题。另校核异常 GPU 时间戳,不能据异常值判断真实停顿。 - 启动红色诊断:逐项复现 vpool、预排布纹理回退、无附件通道和补帧 UI 层缺少 colour-0 的场景,检查实际目标和 UI 遮挡,不能仅以未崩溃判定无害。 - 物理 2K/4K 和高刷新率显示器、连续拖动缩放、快速多次切档及更多运动场景的插值质量;当前物理显示器为 1080p/60Hz,4K drawable 只证明真实 4K 输出纹理路径。 - 独立零售本体 NSP 的完整构建和游玩;已有 XCI 与生成 common-ticket NSP 的构建验证不代替这一项。更新存档能否回退旧版本也未验证,继续保留升级前备份。 - 延续旧性能实验的待做与复测要求,见 [Mac 性能待做](performance-followups.md);旧绘制准备/提交预算的小差异及未命中的复制组不能算作本轮提帧。 ## 本机产物位置调整(2026-10-18) 上文 `MacNativeGames/out/Tears of the Kingdom.app` 为历史路径;当前 App 已移到项目外 `/Volumes/mac-APFS/MacNativeGames-artifacts/apps/Tears of the Kingdom.app`,原桌面唯一入口直接指向新位置。解包、重写、编译产物和临时开发环境也已移出源码目录;正式存档路径不变。原始分析证据在外部按日期归档,源码中只留实现、测试、说明和必要依赖。