# STEP-099 RenderSource 就绪与 RenderSegment 失效诊断 状态:**CLOSED / PASS** 日期:2026-10-06(JST) ## 基线 - 仓库:`/Users/tei/dev/MapLibreParticleLab-step099` - 分支:`step099-render-source-diagnostics` - frozen release:`maplibre-particle/rn/13.4.1-particle.17` - frozen release commit:`e8b451fe70054107773e82484c80e90df3889147` - implementation commit:`8a5214d92dd2f8899db76d2194da42999fa0c87d` - Integration/NavSea:未修改。 ## 本 Step 完成内容 - Renderer readiness snapshot additive 暴露 style/image manager/render orchestrator/sprite readiness、fading、global content revision、per-source 状态和最近 mutation。 - per-source diagnostics 暴露 sourceId/type/enabled/isLoaded;tile-backed source 提供 tileCount/incomplete/loaded/fading。 - RenderSegment diagnostics 只读推导 `dependentSourceIds` / `unknownDependencies`;source-less layer 不制造 source dependency,共享 source 可出现在多个 segment。 - mutation sequence/source/tile action/reason 只消费既有 renderer 事件;正式 revision 增长逻辑不变。 - 正式 loaded 判定保持: `renderTreeParameters->loaded = updateParameters->styleLoaded && isLoaded();` - diagnostics snapshot 不进入正式 loaded/cache/fast path/segment execution。 - RN 仅增加 diagnostics 类型字段;没有 NavSea 私有 API,没有改变业务 API。 - Android Test App 提供 T1 STATIC、T2 GEOJSON_1HZ、T3 PERMANENT_UNREADY、T4 R-C-R。 ## Native 验证 Metal host `RenderSegmentDiagnostics.*`: - 6/6 PASS; - 新增 `ReadinessSnapshotMapsSourcesToSegmentsWithoutSideEffects` PASS; - 覆盖 source readiness、tile stats、source→segment、shared source、source-less 与重复 query 无副作用。 最终源码再次尝试 Metal configure 时当前 Mac 缺 `libuv` pkg-config,因此第二次 host rerun 未进入 build;不虚报 PASS。最终代码已经被 Android 全量 native build 成功编译并由 T1~T4 运行覆盖。 Vulkan:因当前 Mac 缺 `libuv`,NOT RUN tests。 Jest:NOT RUN,用户未要求。 ## Android 构建 首轮 Android NDK 暴露: ```text error: missing field 'tileCount' initializer [-Werror,-Wmissing-designated-field-initializers] ``` 只补全 `RenderSourceReadinessStats` 默认空字段显式初始化。 随后 native build 通过,但 strip 阶段因 Mac 磁盘仅剩约 2 GiB: ```text No space left on device ``` 仅清理可再生 Gradle 9.5.1 cache / 失败中间产物后恢复约 12 GiB,再次增量构建: - `BUILD SUCCESSFUL in 4m 54s` - 86 actionable tasks:7 executed / 79 up-to-date - OpenGL Debug / x86_64 - APK:61,923,024 bytes - libmaplibre.so:39,602,856 bytes - SHA-256: `5de73f0358b835a78d3ae7e2ac22f4efdb2a66775b8c63654a8bb7313957d90a` 设备: - `192.168.200.139:5555` - FZG1-2 - Android 13 / API 33 / x86_64 ## T1~T4 结果 ### T1 STATIC - 1 个启动 false window; - avg/max = 204 ms; - 随后 global loaded 稳定为 true; - retained cache 可持续 reuse; - fast path 可进入 active。 ### T2 GEOJSON_1HZ - 两个 GeoJSON source 交错更新,总 update 122 次,约 61 次/源; - 10 个 loaded=false window; - avg 203 ms; - max 205 ms; - open 0; - 约 60 s 中 false 总时长约 2.03 s,约 3.4%。 结论:存在 readiness thrash,但不是“几乎永远 false”。 ### T3 PERMANENT_UNREADY `step099-unready-raster-array`: - 无 layer; - enabled=false; - isLoaded=false; - 其他主要 source loaded。 结果: - global renderOrchestratorLoaded=false 持续; - 观察结束 open false window = 21,459 ms; - retained cache 持续 style-not-loaded fallback。 结论:单个与 segment 无关的 permanently unready source 足以拉低 V17 全局 loaded。 ### T4 R-C-R - R0 deps=[step099-static] - C deps=[particle-fixture] - R1 deps=[org.maplibre.annotations, step099-dynamic, step099-static] - dynamic update:61 次 结果: - loaded false windows:4 - avg 203 ms - max 207 ms - contentRevision:1760 → 2540 - diagnostics 对 dynamic source 正确给出 affected=[2] - R0 不依赖 dynamic source,但: - build 1 → 121 - invalidation 20 → 200 结论:dependency diagnostics 已能表达 source-local 影响,但 V17 正式 content revision/cache invalidation 仍为 global。 ## Q1~Q7 1. Q1:**YES**。permanently unloaded source 会造成全局 style-not-loaded。 2. Q2:**YES**。1 Hz GeoJSON 会产生约 200 ms readiness false window;T2 avg 203 / max 205 ms。 3. Q3:**NO(本次观测)**。双 1 Hz source 并未让 global loaded 几乎始终 false。 4. Q4:**YES**。GeoJSON tile mutation 推进 global content revision,并使 retained cache 重建/失效。 5. Q5:**YES**。不引用动态 GeoJSON 的 R0 仍受 global revision 影响;源码还确认 `retainedRangeIsStable` 全局检查所有 source fading。 6. Q6:fast path 仍有 global update signature(sources/layers/images 等)、global content revision、global fade、pendingChanges、placement/light/non-particle transition/repaint、annotation mutation、retained revision/cache/topology/boundary,以及 singleContinuous gate。 7. Q7 blocker: - runtime confirmed:`PERMANENT_SOURCE_UNLOADED`、`DYNAMIC_SOURCE_READINESS_THRASH`、`GLOBAL_CONTENT_REVISION` - source confirmed:`GLOBAL_FADE`、`GLOBAL_UPDATE_SIGNATURE` - OTHER:pending/placement/light/repaint/annotation/topology/boundary/singleContinuous。 ## 证据 忽略目录: `artifacts/maplibre-particle/android-validation/` - `build-info.txt` - `build.log` - `MapLibreAndroidTestApp-validation-opengl-debug-x86_64.apk` - `step099-logcat.txt` - `step099-device-info.txt` - `step099-metal-gtest.log` STEP-099 log SHA-256: `0f15d75dde77e7dd2f4d98c46a4c1501a628205e8aae62130b0cb96db6b8482f` ## 边界结论 本 Step 未修改: - retained cache 算法; - fast path gate; - singleContinuous / multi-continuous; - segment-local invalidation 正式语义; - RenderSegment execution; - NavSea; - Source isLoaded; - RN 业务 API。 下一阶段应拆分处理 global source readiness、source/segment-local content revision/invalidation,再独立处理 fast-path global gates。